diff options
Diffstat (limited to 'target/foxboard')
-rw-r--r-- | target/foxboard/Makefile | 38 | ||||
-rw-r--r-- | target/foxboard/device.mk | 7 | ||||
-rw-r--r-- | target/foxboard/files/etc/mdev.conf | 10 | ||||
-rw-r--r-- | target/foxboard/kernel.config | 815 | ||||
-rw-r--r-- | target/foxboard/patches/cris.patch | 5751 | ||||
-rw-r--r-- | target/foxboard/patches/mtd-root.patch | 62 | ||||
-rwxr-xr-x | target/foxboard/tools/boot_linux | 512 | ||||
-rw-r--r-- | target/foxboard/tools/e100boot/Makefile | 26 | ||||
-rw-r--r-- | target/foxboard/tools/mkfimage/Makefile | 4 | ||||
-rwxr-xr-x | target/foxboard/tools/mkfimage/mkfimage | bin | 0 -> 11901 bytes | |||
-rw-r--r-- | target/foxboard/tools/mkfimage/mkfimage.c | 72 | ||||
-rw-r--r-- | target/foxboard/tools/rules.mk | 9 | ||||
-rw-r--r-- | target/foxboard/tools/squashfs/Makefile | 27 | ||||
-rw-r--r-- | target/foxboard/uclibc.config | 241 |
14 files changed, 7574 insertions, 0 deletions
diff --git a/target/foxboard/Makefile b/target/foxboard/Makefile new file mode 100644 index 000000000..1db02a2c9 --- /dev/null +++ b/target/foxboard/Makefile @@ -0,0 +1,38 @@ +# $Id: Makefile 30 2008-09-04 13:31:09Z wbx $ +#- +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include $(TOPDIR)/rules.mk +include $(TOPDIR)/mk/kernel.mk +include $(TOPDIR)/mk/modules.mk +include $(TOPDIR)/mk/kernel-build.mk +include $(TOPDIR)/mk/image.mk + +$(TOOLS_BUILD_DIR): + mkdir -p $(TOOLS_BUILD_DIR) + +tools-compile: $(TOOLS_BUILD_DIR) + $(MAKE) -C tools/mkfimage + $(MAKE) -C tools/e100boot prepare compile install $(MAKE_TRACE) + $(MAKE) -C tools/squashfs prepare compile install + $(INSTALL_BIN) tools/boot_linux $(BIN_DIR)/ + +kernel-install: tools-compile + PATH='${TARGET_PATH}' \ + mkfimage $(LINUX_DIR)/arch/cris/arch-v10/boot/zImage \ + $(BIN_DIR)/${DEVICE}-${ARCH}-kernel $(MAKE_TRACE) + +ifeq ($(FS),squashfs) +imageinstall: $(BIN_DIR)/$(ROOTFSSQUASHFS) + @echo + @echo Use sudo ./boot_linux -F -i $(ROOTFSSQUASHFS) to flash + @echo Do not forget to set network boot jumper, before you start the foxboard +endif +ifeq ($(FS),nfsroot) +imageinstall: ${BIN_DIR}/${ROOTFSTARBALL} + @echo + @echo Use sudo ./boot_linux -F -i ${DEVICE}-${ARCH}-kernel to flash the kernel + @echo Do not forget to set network boot jumper, before you start the foxboard + @echo ${ROOTFSTARBALL} is your nfs root and can be extracted on your nfs server +endif diff --git a/target/foxboard/device.mk b/target/foxboard/device.mk new file mode 100644 index 000000000..0c7504fbd --- /dev/null +++ b/target/foxboard/device.mk @@ -0,0 +1,7 @@ +ARCH:= cris +CPU_ARCH:= cris +KERNEL_VERSION:= 2.6.29.1 +KERNEL_RELEASE:= 1 +KERNEL_MD5SUM:= 4ada43caecb08fe2af71b416b6f586d8 +TARGET_OPTIMIZATION:= -Os -pipe -fno-peephole2 +TARGET_CFLAGS_ARCH:= -march=v10 diff --git a/target/foxboard/files/etc/mdev.conf b/target/foxboard/files/etc/mdev.conf new file mode 100644 index 000000000..178ba51e8 --- /dev/null +++ b/target/foxboard/files/etc/mdev.conf @@ -0,0 +1,10 @@ +null 0:0 777 +zero 0:0 666 +console 0:0 0600 +u?random 0:0 644 +ptmx 0:0 666 +tty 0:0 666 +ttyS* 0:0 640 +device-mapper 0:0 660 @mkdir /dev/mapper +tun 0:0 660 >net/tun +.* 0:0 644 @/lib/mdev/init diff --git a/target/foxboard/kernel.config b/target/foxboard/kernel.config new file mode 100644 index 000000000..3cd568a60 --- /dev/null +++ b/target/foxboard/kernel.config @@ -0,0 +1,815 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.28 +# Tue Dec 30 19:37:01 2008 +# +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 +CONFIG_CRIS=y +CONFIG_HZ=100 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_GROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_AIO is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set +# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" +CONFIG_CLASSIC_RCU=y +# CONFIG_FREEZER is not set + +# +# General setup +# +CONFIG_BINFMT_ELF=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_ETRAX_CMDLINE="init=/init console=ttyS0,115200" +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_ETRAX_FAST_TIMER=y +# CONFIG_ETRAX_KMALLOCED_MODULES is not set +# CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_UNEVICTABLE_LRU=y + +# +# Hardware setup +# +# CONFIG_ETRAX100LX is not set +CONFIG_ETRAX100LX_V2=y +# CONFIG_SVINTO_SIM is not set +# CONFIG_ETRAXFS is not set +# CONFIG_CRIS_MACH_ARTPEC3 is not set +# CONFIG_ETRAX_VCS_SIM is not set +CONFIG_ETRAX_ARCH_V10=y +# CONFIG_ETRAX_ARCH_V32 is not set +CONFIG_ETRAX_DRAM_SIZE=16 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 +CONFIG_ETRAX_FLASH1_SIZE=0 +# CONFIG_ETRAX_DEBUG_PORT0 is not set +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +CONFIG_ETRAX_DEBUG_PORT_NULL=y + +# +# CRIS v10 options +# +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 +CONFIG_ETRAX_PA_LEDS=y +# CONFIG_ETRAX_PB_LEDS is not set +# CONFIG_ETRAX_CSP0_LEDS is not set +# CONFIG_ETRAX_NO_LEDS is not set +CONFIG_ETRAX_LED1G=2 +CONFIG_ETRAX_LED1R=2 +CONFIG_ETRAX_LED2G=3 +CONFIG_ETRAX_LED2R=3 +CONFIG_ETRAX_LED3G=2 +CONFIG_ETRAX_LED3R=2 +CONFIG_ETRAX_RESCUE_SER0=y +# CONFIG_ETRAX_RESCUE_SER1 is not set +# CONFIG_ETRAX_RESCUE_SER2 is not set +# CONFIG_ETRAX_RESCUE_SER3 is not set +CONFIG_ETRAX_DEF_R_WAITSTATES=0x95f8 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x4 +CONFIG_ETRAX_SDRAM=y +CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x09603737 +CONFIG_ETRAX_DEF_R_SDRAM_TIMING=0x80608002 +CONFIG_ETRAX_DEF_R_PORT_PA_DIR=0x1c +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0xf0 +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=0x00 +CONFIG_ETRAX_DEF_R_PORT_PB_DIR=0xce +CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0x03 +# CONFIG_ETRAX_SOFT_SHUTDOWN is not set +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_ASK_IP_FIB_HASH is not set +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +# CONFIG_DEFAULT_BIC is not set +# CONFIG_DEFAULT_CUBIC is not set +# CONFIG_DEFAULT_HTCP is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_WESTWOOD is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +# CONFIG_PHONET is not set +# CONFIG_WIRELESS is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Drivers for built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +CONFIG_ETRAX_SERIAL=y +# CONFIG_ETRAX_SERIAL_FAST_TIMER is not set +# CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is not set +CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS=1 +CONFIG_ETRAX_SERIAL_PORT0=y +CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE=y +# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA is not set +# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set +# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED is not set +CONFIG_ETRAX_SER0_DTR_ON_PA_BIT=-1 +CONFIG_ETRAX_SER0_RI_ON_PA_BIT=-1 +CONFIG_ETRAX_SER0_DSR_ON_PA_BIT=-1 +CONFIG_ETRAX_SER0_CD_ON_PA_BIT=-1 +CONFIG_ETRAX_SER0_DTR_ON_PB_BIT=-1 +CONFIG_ETRAX_SER0_RI_ON_PB_BIT=-1 +CONFIG_ETRAX_SER0_DSR_ON_PB_BIT=-1 +CONFIG_ETRAX_SER0_CD_ON_PB_BIT=-1 +# CONFIG_ETRAX_SERIAL_PORT1 is not set +# CONFIG_ETRAX_SERIAL_PORT2 is not set +# CONFIG_ETRAX_SERIAL_PORT3 is not set +# CONFIG_ETRAX_RS485 is not set +CONFIG_ETRAX_USB_HOST=y +CONFIG_ETRAX_USB_HOST_PORT1=y +CONFIG_ETRAX_USB_HOST_PORT2=y +CONFIG_ETRAX_PTABLE_SECTOR=0 +CONFIG_ETRAX_I2C=y +CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y +CONFIG_ETRAX_I2C_DATA_PORT=0 +CONFIG_ETRAX_I2C_CLK_PORT=1 +# CONFIG_ETRAX_I2C_EEPROM is not set +CONFIG_ETRAX_GPIO=y +CONFIG_ETRAX_PA_BUTTON_BITMASK=02 +CONFIG_ETRAX_PA_CHANGEABLE_DIR=0xFF +CONFIG_ETRAX_PA_CHANGEABLE_BITS=0xFF +CONFIG_ETRAX_PB_CHANGEABLE_DIR=0xFF +CONFIG_ETRAX_PB_CHANGEABLE_BITS=0xFF +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +CONFIG_ETRAX_AXISFLASHMAP=y +# CONFIG_ETRAX_RTC is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y +CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT=y +# CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT is not set +# CONFIG_ETRAX_SERIAL_PORT0_DMA0_OUT is not set +CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN=y +# CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN is not set +# CONFIG_ETRAX_SERIAL_PORT0_DMA1_IN is not set + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_ROOTFS_ROOT_DEV=y +CONFIG_MTD_ROOTFS_SPLIT=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +# CONFIG_MTD_BLOCK is not set +CONFIG_MTD_BLOCK_RO=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_GEOMETRY is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_RAM=y +CONFIG_MTD_ROM=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_BLK_DEV_HD is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_DAB is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_IWLWIFI_LEDS is not set + +# +# USB Network Adapters +# +CONFIG_USB_CATC=m +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_I2C is not set +# CONFIG_RTC_CLASS is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set +# CONFIG_DLM is not set +CONFIG_USB_SUPPORT=y +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +# CONFIG_USB_GADGET is not set + +# +# Kernel hacking +# +# CONFIG_SYSTEM_PROFILER is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set + +# +# Tracers +# +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_MANAGER2 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set + +# +# OCF Configuration +# +# CONFIG_OCF_OCF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y diff --git a/target/foxboard/patches/cris.patch b/target/foxboard/patches/cris.patch new file mode 100644 index 000000000..e0522d674 --- /dev/null +++ b/target/foxboard/patches/cris.patch @@ -0,0 +1,5751 @@ +diff -Nur linux-2.6.29.1.orig/arch/cris/Kconfig linux-2.6.29.1/arch/cris/Kconfig +--- linux-2.6.29.1.orig/arch/cris/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/Kconfig 2009-05-09 01:52:49.814118199 +0200 +@@ -168,6 +168,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 +@@ -273,7 +279,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 +@@ -662,6 +668,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.29.1.orig/arch/cris/Makefile linux-2.6.29.1/arch/cris/Makefile +--- linux-2.6.29.1.orig/arch/cris/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/Makefile 2009-05-09 01:53:12.795556299 +0200 +@@ -40,12 +40,12 @@ + + LD = $(CROSS_COMPILE)ld -mcrislinux + +-OBJCOPYFLAGS := -O binary -R .note -R .comment -S ++OBJCOPYFLAGS := -O binary -R .note -R .note.gnu.build-id -R .comment + + CPPFLAGS_vmlinux.lds = -DDRAM_VIRTUAL_BASE=0x$(CONFIG_ETRAX_DRAM_VIRTUAL_BASE) + + 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.29.1.orig/arch/cris/arch-v10/boot/Makefile linux-2.6.29.1/arch/cris/arch-v10/boot/Makefile +--- linux-2.6.29.1.orig/arch/cris/arch-v10/boot/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/boot/Makefile 2009-05-09 01:52:50.030131736 +0200 +@@ -2,10 +2,8 @@ + # arch/cris/arch-v10/boot/Makefile + # + +-OBJCOPYFLAGS = -O binary --remove-section=.bss +- + subdir- := compressed rescue +-targets := Image ++targets := Image zImage + + $(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) +@@ -13,7 +11,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.29.1.orig/arch/cris/arch-v10/boot/compressed/Makefile linux-2.6.29.1/arch/cris/arch-v10/boot/compressed/Makefile +--- linux-2.6.29.1.orig/arch/cris/arch-v10/boot/compressed/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/boot/compressed/Makefile 2009-05-09 01:52:50.102136724 +0200 +@@ -6,7 +6,6 @@ + ccflags-y += -O2 $(LINUXINCLUDE) + ldflags-y += -T $(srctree)/$(src)/decompress.lds + OBJECTS = $(obj)/head.o $(obj)/misc.o +-OBJCOPYFLAGS = -O binary --remove-section=.bss + + quiet_cmd_image = BUILD $@ + cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ +diff -Nur linux-2.6.29.1.orig/arch/cris/arch-v10/boot/compressed/misc.c linux-2.6.29.1/arch/cris/arch-v10/boot/compressed/misc.c +--- linux-2.6.29.1.orig/arch/cris/arch-v10/boot/compressed/misc.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/boot/compressed/misc.c 2009-05-09 01:52:50.126138666 +0200 +@@ -5,7 +5,7 @@ + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 +- * puts by Nick Holloway 1993, better puts by Martin Mares 1995 ++ * putstr by Nick Holloway 1993, better putstr by Martin Mares 1995 + * adaptation for Linux/CRIS Axis Communications AB, 1999 + * + */ +@@ -102,7 +102,7 @@ + static long bytes_out = 0; + static uch *output_data; + static unsigned long output_ptr = 0; +-static void puts(const char *); ++static void putstr(const char *); + + /* the "heap" is put directly after the BSS ends, at end */ + +@@ -115,7 +115,7 @@ + /* decompressor info and error messages to serial console */ + + static void +-puts(const char *s) ++putstr(const char *s) + { + #ifndef CONFIG_ETRAX_DEBUG_PORT_NULL + while (*s) { +@@ -188,9 +188,9 @@ + + static void error(char *x) + { +- puts("\n\n"); +- puts(x); +- puts("\n\n -- System halted\n"); ++ putstr("\n\n"); ++ putstr(x); ++ putstr("\n\n -- System halted\n"); + + while (1); /* Halt */ + } +@@ -234,13 +234,7 @@ + + makecrc(); + +- __asm__ volatile ("move $vr,%0" : "=rm" (revision)); +- if (revision < 10) { +- puts("You need an ETRAX 100LX to run linux 2.6\n"); +- while (1); +- } +- +- puts("Uncompressing Linux...\n"); ++ putstr("Uncompressing Linux...\n"); + gunzip(); +- puts("Done. Now booting the kernel.\n"); ++ putstr("Done. Now booting the kernel.\n"); + } +diff -Nur linux-2.6.29.1.orig/arch/cris/arch-v10/drivers/axisflashmap.c linux-2.6.29.1/arch/cris/arch-v10/drivers/axisflashmap.c +--- linux-2.6.29.1.orig/arch/cris/arch-v10/drivers/axisflashmap.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/drivers/axisflashmap.c 2009-05-09 01:52:50.210144343 +0200 +@@ -122,19 +122,19 @@ + */ + 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 + } + }; + +@@ -281,6 +281,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 +297,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 +390,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 +412,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.29.1.orig/arch/cris/arch-v10/drivers/ds1302.c linux-2.6.29.1/arch/cris/arch-v10/drivers/ds1302.c +--- linux-2.6.29.1.orig/arch/cris/arch-v10/drivers/ds1302.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/drivers/ds1302.c 2009-05-09 01:52:50.538166563 +0200 +@@ -21,6 +21,7 @@ + #include <linux/delay.h> + #include <linux/bcd.h> + #include <linux/capability.h> ++#include <linux/device.h> + + #include <asm/uaccess.h> + #include <asm/system.h> +@@ -489,6 +490,10 @@ + return 0; + } + ++#ifdef CONFIG_SYSFS ++static struct class *rtc_class; ++#endif ++ + static int __init ds1302_register(void) + { + ds1302_init(); +@@ -497,6 +502,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.29.1.orig/arch/cris/arch-v10/drivers/gpio.c linux-2.6.29.1/arch/cris/arch-v10/drivers/gpio.c +--- linux-2.6.29.1.orig/arch/cris/arch-v10/drivers/gpio.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/drivers/gpio.c 2009-05-09 01:52:50.586168771 +0200 +@@ -21,6 +21,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> +@@ -771,6 +772,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; +@@ -784,6 +789,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.29.1.orig/arch/cris/arch-v10/lib/hw_settings.S linux-2.6.29.1/arch/cris/arch-v10/lib/hw_settings.S +--- linux-2.6.29.1.orig/arch/cris/arch-v10/lib/hw_settings.S 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/lib/hw_settings.S 2009-05-09 01:52:50.690174762 +0200 +@@ -60,3 +60,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.29.1.orig/arch/cris/arch-v10/mm/init.c linux-2.6.29.1/arch/cris/arch-v10/mm/init.c +--- linux-2.6.29.1.orig/arch/cris/arch-v10/mm/init.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/arch/cris/arch-v10/mm/init.c 2009-05-09 01:52:50.754179285 +0200 +@@ -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.29.1.orig/drivers/net/cris/eth_v10.c linux-2.6.29.1/drivers/net/cris/eth_v10.c +--- linux-2.6.29.1.orig/drivers/net/cris/eth_v10.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/net/cris/eth_v10.c 2009-05-09 01:52:50.754179285 +0200 +@@ -1705,7 +1705,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.29.1.orig/drivers/serial/crisv10.c linux-2.6.29.1/drivers/serial/crisv10.c +--- linux-2.6.29.1.orig/drivers/serial/crisv10.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/serial/crisv10.c 2009-05-09 01:52:50.758178956 +0200 +@@ -27,6 +27,7 @@ + #include <linux/kernel.h> + #include <linux/mutex.h> + #include <linux/bitops.h> ++#include <linux/device.h> + + #include <asm/io.h> + #include <asm/irq.h> +@@ -4393,6 +4394,7 @@ + .tiocmset = rs_tiocmset + }; + ++static struct class *rs_class; + static int __init + rs_init(void) + { +@@ -4527,6 +4529,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.29.1.orig/drivers/usb/Makefile linux-2.6.29.1/drivers/usb/Makefile +--- linux-2.6.29.1.orig/drivers/usb/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/usb/Makefile 2009-05-09 01:52:50.758178956 +0200 +@@ -19,6 +19,7 @@ + obj-$(CONFIG_USB_U132_HCD) += host/ + obj-$(CONFIG_USB_R8A66597_HCD) += host/ + obj-$(CONFIG_USB_HWA_HCD) += host/ ++obj-$(CONFIG_ETRAX_USB_HOST) += host/ + + obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ + +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/Makefile linux-2.6.29.1/drivers/usb/host/Makefile +--- linux-2.6.29.1.orig/drivers/usb/host/Makefile 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/usb/host/Makefile 2009-05-09 01:52:50.758178956 +0200 +@@ -29,3 +29,4 @@ + 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 +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/hc-cris-dbg.h linux-2.6.29.1/drivers/usb/host/hc-cris-dbg.h +--- linux-2.6.29.1.orig/drivers/usb/host/hc-cris-dbg.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/usb/host/hc-cris-dbg.h 2009-05-09 01:52:50.762178907 +0200 +@@ -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.29.1.orig/drivers/usb/host/hc-crisv10.c linux-2.6.29.1/drivers/usb/host/hc-crisv10.c +--- linux-2.6.29.1.orig/drivers/usb/host/hc-crisv10.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/usb/host/hc-crisv10.c 2009-05-09 01:52:50.834182770 +0200 +@@ -0,0 +1,4800 @@ ++/* ++ * ++ * 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/spinlock.h> ++#include <linux/usb.h> ++#include <linux/platform_device.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <arch/dma.h> ++#include <arch/io_interface_mux.h> ++ ++#include "../core/hcd.h" ++#include "../core/hub.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 (2.6.25-rc9 port)" ++ ++#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("ClearPortFeature: suspend\n"); ++ break; ++ case USB_PORT_FEAT_POWER: ++ rh_dbg("ClearPortFeature: power\n"); ++ break; ++ ++ case USB_PORT_FEAT_C_ENABLE: ++ rh_dbg("ClearPortFeature: c_enable\n"); ++ goto clear; ++ case USB_PORT_FEAT_C_SUSPEND: ++ rh_dbg("ClearPortFeature: c_suspend\n"); ++ goto clear; ++ case USB_PORT_FEAT_C_CONNECTION: ++ rh_dbg("ClearPortFeature: c_connection\n"); ++ goto clear; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ rh_dbg("ClearPortFeature: c_over_current\n"); ++ goto clear; ++ case USB_PORT_FEAT_C_RESET: ++ rh_dbg("ClearPortFeature: c_reset\n"); ++ goto clear; ++ clear: ++ rh.wPortChange[bPort] &= ~(1 << (wFeature - 16)); ++ break; ++ default: ++ rh_dbg("ClearPortFeature: unknown feature\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++/* Handle a suspend request for the root hub (called from hcd_driver) */ ++static int rh_suspend_request(struct usb_hcd *hcd) ++{ ++ return 0; /* no-op for now */ ++} ++ ++/* Handle a resume request for the root hub (called from hcd_driver) */ ++static int rh_resume_request(struct usb_hcd *hcd) ++{ ++ return 0; /* no-op for now */ ++} ++#endif /* CONFIG_PM */ ++ ++ ++ ++/* Wrapper function for workaround port disable registers in USB controller */ ++static void rh_disable_port(unsigned int port) { ++ volatile int timeout = 10000; ++ volatile char* usb_portx_disable; ++ switch(port) { ++ case 0: ++ usb_portx_disable = R_USB_PORT1_DISABLE; ++ break; ++ case 1: ++ usb_portx_disable = R_USB_PORT2_DISABLE; ++ break; ++ default: ++ /* Invalid port index */ ++ return; ++ } ++ /* Set disable flag in special register */ ++ *usb_portx_disable = IO_STATE(R_USB_PORT1_DISABLE, disable, yes); ++ /* Wait until not enabled anymore */ ++ while((rh.wPortStatusPrev[port] & ++ IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for port %d to become disabled\n", port); ++ } ++ /* clear disable flag in special register */ ++ *usb_portx_disable = IO_STATE(R_USB_PORT1_DISABLE, disable, no); ++ rh_info("Physical port %d disabled\n", port+1); ++} ++ ++ ++/******************************************************************/ ++/* Transfer Controller (TC) functions */ ++/******************************************************************/ ++ ++/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it ++ dynamically? ++ To adjust it dynamically we would have to get an interrupt when we reach ++ the end of the rx descriptor list, or when we get close to the end, and ++ then allocate more descriptors. */ ++#define NBR_OF_RX_DESC 512 ++#define RX_DESC_BUF_SIZE 1024 ++#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE) ++ ++ ++/* Local variables for Transfer Controller */ ++/* --------------------------------------- */ ++ ++/* This is a circular (double-linked) list of the active urbs for each epid. ++ The head is never removed, and new urbs are linked onto the list as ++ urb_entry_t elements. Don't reference urb_list directly; use the wrapper ++ functions instead (which includes spin_locks) */ ++static struct list_head urb_list[NBR_OF_EPIDS]; ++ ++/* Read about the need and usage of this lock in submit_ctrl_urb. */ ++/* Lock for URB lists for each EPID */ ++static spinlock_t urb_list_lock; ++ ++/* Lock for EPID array register (R_USB_EPT_x) in Etrax */ ++static spinlock_t etrax_epid_lock; ++ ++/* Lock for dma8 sub0 handling */ ++static spinlock_t etrax_dma8_sub0_lock; ++ ++/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line. ++ Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be ++ cache aligned. */ ++static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32))); ++static volatile struct USB_IN_Desc RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); ++ ++/* Pointers into RxDescList. */ ++static volatile struct USB_IN_Desc *myNextRxDesc; ++static volatile struct USB_IN_Desc *myLastRxDesc; ++ ++/* A zout transfer makes a memory access at the address of its buf pointer, ++ which means that setting this buf pointer to 0 will cause an access to the ++ flash. In addition to this, setting sw_len to 0 results in a 16/32 bytes ++ (depending on DMA burst size) transfer. ++ Instead, we set it to 1, and point it to this buffer. */ ++static int zout_buffer[4] __attribute__ ((aligned (4))); ++ ++/* Cache for allocating new EP and SB descriptors. */ ++//static kmem_cache_t *usb_desc_cache; ++static struct kmem_cache *usb_desc_cache; ++ ++/* Cache for the data allocated in the isoc descr top half. */ ++//static kmem_cache_t *isoc_compl_cache; ++static struct kmem_cache *isoc_compl_cache; ++ ++/* Cache for the data allocated when delayed finishing of URBs */ ++//static kmem_cache_t *later_data_cache; ++static struct kmem_cache *later_data_cache; ++ ++/* Counter to keep track of how many Isoc EP we have sat up. Used to enable ++ and disable iso_eof interrupt. We only need these interrupts when we have ++ Isoc data endpoints (consumes CPU cycles). ++ FIXME: This could be more fine granular, so this interrupt is only enabled ++ when we have a In Isoc URB not URB_ISO_ASAP flaged queued. */ ++static int isoc_epid_counter; ++ ++/* Protecting wrapper functions for R_USB_EPT_x */ ++/* -------------------------------------------- */ ++static inline void etrax_epid_set(__u8 index, __u32 data) { ++ unsigned long flags; ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, index); ++ nop(); ++ *R_USB_EPT_DATA = data; ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++} ++ ++static inline void etrax_epid_clear_error(__u8 index) { ++ unsigned long flags; ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, index); ++ nop(); ++ *R_USB_EPT_DATA &= ++ ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | ++ IO_MASK(R_USB_EPT_DATA, error_count_out) | ++ IO_MASK(R_USB_EPT_DATA, error_code)); ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++} ++ ++static inline void etrax_epid_set_toggle(__u8 index, __u8 dirout, ++ __u8 toggle) { ++ unsigned long flags; ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, index); ++ nop(); ++ if(dirout) { ++ *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out); ++ *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle); ++ } else { ++ *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in); ++ *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle); ++ } ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++} ++ ++static inline __u8 etrax_epid_get_toggle(__u8 index, __u8 dirout) { ++ unsigned long flags; ++ __u8 toggle; ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, index); ++ nop(); ++ if (dirout) { ++ toggle = IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA); ++ } else { ++ toggle = IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA); ++ } ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++ return toggle; ++} ++ ++ ++static inline __u32 etrax_epid_get(__u8 index) { ++ unsigned long flags; ++ __u32 data; ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, index); ++ nop(); ++ data = *R_USB_EPT_DATA; ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++ return data; ++} ++ ++ ++ ++ ++/* Main functions for Transfer Controller */ ++/* -------------------------------------- */ ++ ++/* Init structs, memories and lists used by Transfer Controller */ ++int tc_init(struct usb_hcd *hcd) { ++ int i; ++ /* Clear software state info for all epids */ ++ memset(epid_state, 0, sizeof(struct etrax_epid) * NBR_OF_EPIDS); ++ ++ /* Set Invalid and Dummy as being in use and disabled */ ++ epid_state[INVALID_EPID].inuse = 1; ++ epid_state[DUMMY_EPID].inuse = 1; ++ epid_state[INVALID_EPID].disabled = 1; ++ epid_state[DUMMY_EPID].disabled = 1; ++ ++ /* Clear counter for how many Isoc epids we have sat up */ ++ isoc_epid_counter = 0; ++ ++ /* Initialize the urb list by initiating a head for each list. ++ Also reset list hodling active URB for each epid */ ++ for (i = 0; i < NBR_OF_EPIDS; i++) { ++ INIT_LIST_HEAD(&urb_list[i]); ++ activeUrbList[i] = NULL; ++ } ++ ++ /* Init lock for URB lists */ ++ spin_lock_init(&urb_list_lock); ++ /* Init lock for Etrax R_USB_EPT register */ ++ spin_lock_init(&etrax_epid_lock); ++ /* Init lock for Etrax dma8 sub0 handling */ ++ spin_lock_init(&etrax_dma8_sub0_lock); ++ ++ /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */ ++ ++ /* Note that we specify sizeof(struct USB_EP_Desc) as the size, but also ++ allocate SB descriptors from this cache. This is ok since ++ sizeof(struct USB_EP_Desc) == sizeof(struct USB_SB_Desc). */ ++// usb_desc_cache = kmem_cache_create("usb_desc_cache", ++// sizeof(struct USB_EP_Desc), 0, ++// SLAB_HWCACHE_ALIGN, 0, 0); ++ usb_desc_cache = kmem_cache_create( ++ "usb_desc_cache", ++ sizeof(struct USB_EP_Desc), ++ 0, ++ SLAB_HWCACHE_ALIGN, ++ NULL); ++ if(usb_desc_cache == NULL) { ++ return -ENOMEM; ++ } ++ ++ /* Create slab cache for speedy allocation of memory for isoc bottom-half ++ interrupt handling */ ++// isoc_compl_cache = ++// kmem_cache_create("isoc_compl_cache", ++// sizeof(struct crisv10_isoc_complete_data), ++// 0, SLAB_HWCACHE_ALIGN, 0, 0); ++ isoc_compl_cache = kmem_cache_create( ++ "isoc_compl_cache", ++ sizeof(struct crisv10_isoc_complete_data), ++ 0, ++ SLAB_HWCACHE_ALIGN, ++ NULL ++ ); ++ ++ if(isoc_compl_cache == NULL) { ++ return -ENOMEM; ++ } ++ ++ /* Create slab cache for speedy allocation of memory for later URB finish ++ struct */ ++// later_data_cache = ++// kmem_cache_create("later_data_cache", ++// sizeof(struct urb_later_data), ++// 0, SLAB_HWCACHE_ALIGN, 0, 0); ++ ++ later_data_cache = kmem_cache_create( ++ "later_data_cache", ++ sizeof(struct urb_later_data), ++ 0, ++ SLAB_HWCACHE_ALIGN, ++ NULL ++ ); ++ ++ if(later_data_cache == NULL) { ++ return -ENOMEM; ++ } ++ ++ ++ /* Initiate the bulk start timer. */ ++ init_timer(&bulk_start_timer); ++ bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL; ++ bulk_start_timer.function = tc_bulk_start_timer_func; ++ add_timer(&bulk_start_timer); ++ ++ ++ /* Initiate the bulk eot timer. */ ++ init_timer(&bulk_eot_timer); ++ bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL; ++ bulk_eot_timer.function = tc_bulk_eot_timer_func; ++ bulk_eot_timer.data = (unsigned long)hcd; ++ add_timer(&bulk_eot_timer); ++ ++ return 0; ++} ++ ++/* Uninitialize all resources used by Transfer Controller */ ++void tc_destroy(void) { ++ ++ /* Destroy all slab cache */ ++ kmem_cache_destroy(usb_desc_cache); ++ kmem_cache_destroy(isoc_compl_cache); ++ kmem_cache_destroy(later_data_cache); ++ ++ /* Remove timers */ ++ del_timer(&bulk_start_timer); ++ del_timer(&bulk_eot_timer); ++} ++ ++static void restart_dma8_sub0(void) { ++ unsigned long flags; ++ spin_lock_irqsave(&etrax_dma8_sub0_lock, flags); ++ /* Verify that the dma is not running */ ++ if ((*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd)) == 0) { ++ struct USB_EP_Desc *ep = (struct USB_EP_Desc *)phys_to_virt(*R_DMA_CH8_SUB0_EP); ++ while (DUMMY_EPID == IO_EXTRACT(USB_EP_command, epid, ep->command)) { ++ ep = (struct USB_EP_Desc *)phys_to_virt(ep->next); ++ } ++ /* Advance the DMA to the next EP descriptor that is not a DUMMY_EPID. ++ * ep->next is already a physical address. virt_to_phys is needed, see ++ * http://mhonarc.axis.se/dev-etrax/msg08630.html ++ */ ++ //*R_DMA_CH8_SUB0_EP = ep->next; ++ *R_DMA_CH8_SUB0_EP = virt_to_phys(ep); ++ /* Restart the DMA */ ++ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); ++ } ++ spin_unlock_irqrestore(&etrax_dma8_sub0_lock, flags); ++} ++ ++/* queue an URB with the transfer controller (called from hcd_driver) */ ++//static int tc_urb_enqueue(struct usb_hcd *hcd, ++// struct usb_host_endpoint *ep, ++// struct urb *urb, ++// gfp_t mem_flags) { ++static int tc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ++{ ++ int epid; ++ int retval; ++// int bustime = 0; ++ int maxpacket; ++ unsigned long flags; ++ struct crisv10_urb_priv *urb_priv; ++ struct crisv10_hcd* crisv10_hcd = hcd_to_crisv10_hcd(hcd); ++ DBFENTER; ++ ++ if(!(crisv10_hcd->running)) { ++ /* The USB Controller is not running, probably because no device is ++ attached. No idea to enqueue URBs then */ ++ tc_warn("Rejected enqueueing of URB:0x%x because no dev attached\n", ++ (unsigned int)urb); ++ return -ENOENT; ++ } ++ ++ maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++ /* Special case check for In Isoc transfers. Specification states that each ++ In Isoc transfer consists of one packet and therefore it should fit into ++ the transfer-buffer of an URB. ++ We do the check here to be sure (an invalid scenario can be produced with ++ parameters to the usbtest suite) */ ++ if(usb_pipeisoc(urb->pipe) && usb_pipein(urb->pipe) && ++ (urb->transfer_buffer_length < maxpacket)) { ++ tc_err("Submit In Isoc URB with buffer length:%d to pipe with maxpacketlen: %d\n", urb->transfer_buffer_length, maxpacket); ++ return -EMSGSIZE; ++ } ++ ++ /* Check if there is enough bandwidth for periodic transfer */ ++ if(usb_pipeint(urb->pipe) || usb_pipeisoc(urb->pipe)) { ++ /* only check (and later claim) if not already claimed */ ++ if (urb->bandwidth == 0) { ++ bustime = usb_check_bandwidth(urb->dev, urb); ++ if (bustime < 0) { ++ tc_err("Not enough periodic bandwidth\n"); ++ return -ENOSPC; ++ } ++ } ++ } ++#endif ++ ++ /* Check if there is a epid for URBs destination, if not this function ++ set up one. */ ++ //epid = tc_setup_epid(ep, urb, mem_flags); ++ epid = tc_setup_epid(urb, mem_flags); ++ if (epid < 0) { ++ tc_err("Failed setup epid:%d for URB:0x%x\n", epid, (unsigned int)urb); ++ DBFEXIT; ++ return -ENOMEM; ++ } ++ ++ if(urb == activeUrbList[epid]) { ++ tc_err("Resubmition of allready active URB:0x%x\n", (unsigned int)urb); ++ return -ENXIO; ++ } ++ ++ if(urb_list_entry(urb, epid)) { ++ tc_err("Resubmition of allready queued URB:0x%x\n", (unsigned int)urb); ++ return -ENXIO; ++ } ++ ++ /* If we actively have flaged endpoint as disabled then refuse submition */ ++ if(epid_state[epid].disabled) { ++ return -ENOENT; ++ } ++ ++ /* Allocate and init HC-private data for URB */ ++ if(urb_priv_create(hcd, urb, epid, mem_flags) != 0) { ++ DBFEXIT; ++ return -ENOMEM; ++ } ++ urb_priv = urb->hcpriv; ++ ++ tc_dbg("Enqueue URB:0x%x[%d] epid:%d (%s) bufflen:%d\n", ++ (unsigned int)urb, urb_priv->urb_num, epid, ++ pipe_to_str(urb->pipe), urb->transfer_buffer_length); ++ ++ /* Create and link SBs required for this URB */ ++ retval = create_sb_for_urb(urb, mem_flags); ++ if(retval != 0) { ++ tc_err("Failed to create SBs for URB:0x%x[%d]\n", (unsigned int)urb, ++ urb_priv->urb_num); ++ urb_priv_free(hcd, urb); ++ DBFEXIT; ++ return retval; ++ } ++ ++ /* Init intr EP pool if this URB is a INTR transfer. This pool is later ++ used when inserting EPs in the TxIntrEPList. We do the alloc here ++ so we can't run out of memory later */ ++ if(usb_pipeint(urb->pipe)) { ++ retval = init_intr_urb(urb, mem_flags); ++ if(retval != 0) { ++ tc_warn("Failed to init Intr URB\n"); ++ urb_priv_free(hcd, urb); ++ DBFEXIT; ++ return retval; ++ } ++ } ++ ++ /* Disable other access when inserting USB */ ++ ++ /* BUG on sleeping inside int disabled if using local_irq_save/local_irq_restore ++ * her - because urb_list_add() and tc_dma_process_queue() save irqs again !??! ++ */ ++// local_irq_save(flags); ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++ /* Claim bandwidth, if needed */ ++ if(bustime) { ++ usb_claim_bandwidth(urb->dev, urb, bustime, 0); ++ } ++ ++ /* Add URB to EP queue */ ++ urb_list_add(urb, epid, mem_flags); ++ ++ if(usb_pipeisoc(urb->pipe)) { ++ /* Special processing of Isoc URBs. */ ++ tc_dma_process_isoc_urb(urb); ++ } else { ++ /* Process EP queue for rest of the URB types (Bulk, Ctrl, Intr) */ ++ tc_dma_process_queue(epid); ++ } ++#endif ++ /* Add URB to EP queue */ ++ urb_list_add(urb, epid, mem_flags); ++ ++ /*hinko link/unlink urb -> ep */ ++ spin_lock_irqsave(&crisv10_hcd->lock, flags); ++ //spin_lock(&crisv10_hcd->lock); ++ retval = usb_hcd_link_urb_to_ep(hcd, urb); ++ if (retval) { ++ spin_unlock_irqrestore(&crisv10_hcd->lock, flags); ++ tc_warn("Failed to link urb to ep\n"); ++ urb_priv_free(hcd, urb); ++ DBFEXIT; ++ return retval; ++ } ++ spin_unlock_irqrestore(&crisv10_hcd->lock, flags); ++ //spin_unlock(&crisv10_hcd->lock); ++ ++ /* Process EP queue for rest of the URB types (Bulk, Ctrl, Intr) */ ++ tc_dma_process_queue(epid); ++ ++// local_irq_restore(flags); ++ ++ DBFEXIT; ++ return 0; ++} ++ ++/* remove an URB from the transfer controller queues (called from hcd_driver)*/ ++//static int tc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) ++static int tc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ struct crisv10_urb_priv *urb_priv; ++ unsigned long flags; ++ int epid; ++ ++ DBFENTER; ++ /* Disable interrupts here since a descriptor interrupt for the isoc epid ++ will modify the sb list. This could possibly be done more granular, but ++ urb_dequeue should not be used frequently anyway. ++ */ ++ local_irq_save(flags); ++ ++ urb_priv = urb->hcpriv; ++ ++ if (!urb_priv) { ++ /* This happens if a device driver calls unlink on an urb that ++ was never submitted (lazy driver) or if the urb was completed ++ while dequeue was being called. */ ++ tc_warn("Dequeing of not enqueued URB:0x%x\n", (unsigned int)urb); ++ local_irq_restore(flags); ++ return 0; ++ } ++ epid = urb_priv->epid; ++ ++ tc_warn("Dequeing %s URB:0x%x[%d] (%s %s epid:%d) status:%d %s\n", ++ (urb == activeUrbList[epid]) ? "active" : "queued", ++ (unsigned int)urb, urb_priv->urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), epid, urb->status, ++ (urb_priv->later_data) ? "later-sched" : ""); ++ ++ /* For Bulk, Ctrl and Intr are only one URB active at a time. So any URB ++ that isn't active can be dequeued by just removing it from the queue */ ++ if(usb_pipebulk(urb->pipe) || usb_pipecontrol(urb->pipe) || ++ usb_pipeint(urb->pipe)) { ++ ++ /* Check if URB haven't gone further than the queue */ ++ if(urb != activeUrbList[epid]) { ++ ASSERT(urb_priv->later_data == NULL); ++ tc_warn("Dequeing URB:0x%x[%d] (%s %s epid:%d) from queue" ++ " (not active)\n", (unsigned int)urb, urb_priv->urb_num, ++ str_dir(urb->pipe), str_type(urb->pipe), epid); ++ ++ /* Finish the URB with error status from USB core */ ++ tc_finish_urb(hcd, urb, urb->status); ++ local_irq_restore(flags); ++ return 0; ++ } ++ } ++ ++ /* Set URB status to Unlink for handling when interrupt comes. */ ++ urb_priv->urb_state = UNLINK; ++ ++ /* Differentiate dequeing of Bulk and Ctrl from Isoc and Intr */ ++ switch(usb_pipetype(urb->pipe)) { ++ case PIPE_BULK: ++ /* Check if EP still is enabled */ ++ if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ /* The EP was enabled, disable it. */ ++ TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ } ++ /* Kicking dummy list out of the party. */ ++ TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); ++ break; ++ case PIPE_CONTROL: ++ /* Check if EP still is enabled */ ++ if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ /* The EP was enabled, disable it. */ ++ TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ } ++ break; ++ case PIPE_ISOCHRONOUS: ++ /* Disabling, busy-wait and unlinking of Isoc SBs will be done in ++ finish_isoc_urb(). Because there might the case when URB is dequeued ++ but there are other valid URBs waiting */ ++ ++ /* Check if In Isoc EP still is enabled */ ++ if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ /* The EP was enabled, disable it. */ ++ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ } ++ break; ++ case PIPE_INTERRUPT: ++ /* Special care is taken for interrupt URBs. EPs are unlinked in ++ tc_finish_urb */ ++ break; ++ default: ++ break; ++ } ++ ++ /* Asynchronous unlink, finish the URB later from scheduled or other ++ event (data finished, error) */ ++ tc_finish_urb_later(hcd, urb, urb->status); ++ ++ local_irq_restore(flags); ++ DBFEXIT; ++ return 0; ++} ++ ++ ++static void tc_sync_finish_epid(struct usb_hcd *hcd, int epid) { ++ volatile int timeout = 10000; ++ struct urb* urb; ++ struct crisv10_urb_priv* urb_priv; ++ unsigned long flags; ++ ++ volatile struct USB_EP_Desc *first_ep; /* First EP in the list. */ ++ volatile struct USB_EP_Desc *curr_ep; /* Current EP, the iterator. */ ++ volatile struct USB_EP_Desc *next_ep; /* The EP after current. */ ++ ++ int type = epid_state[epid].type; ++ ++ /* Setting this flag will cause enqueue() to return -ENOENT for new ++ submitions on this endpoint and finish_urb() wont process queue further */ ++ epid_state[epid].disabled = 1; ++ ++ switch(type) { ++ case PIPE_BULK: ++ /* Check if EP still is enabled */ ++ if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ /* The EP was enabled, disable it. */ ++ TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ tc_warn("sync_finish: Disabling EP for epid:%d\n", epid); ++ ++ /* Do busy-wait until DMA not using this EP descriptor anymore */ ++ while((*R_DMA_CH8_SUB0_EP == ++ virt_to_phys(&TxBulkEPList[epid])) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for DMA-TX-Bulk to leave EP for" ++ " epid:%d\n", epid); ++ } ++ } ++ break; ++ ++ case PIPE_CONTROL: ++ /* Check if EP still is enabled */ ++ if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ /* The EP was enabled, disable it. */ ++ TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ tc_warn("sync_finish: Disabling EP for epid:%d\n", epid); ++ ++ /* Do busy-wait until DMA not using this EP descriptor anymore */ ++ while((*R_DMA_CH8_SUB1_EP == ++ virt_to_phys(&TxCtrlEPList[epid])) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for DMA-TX-Ctrl to leave EP for" ++ " epid:%d\n", epid); ++ } ++ } ++ break; ++ ++ case PIPE_INTERRUPT: ++ local_irq_save(flags); ++ /* Disable all Intr EPs belonging to epid */ ++ first_ep = &TxIntrEPList[0]; ++ curr_ep = first_ep; ++ do { ++ next_ep = (struct USB_EP_Desc *)phys_to_virt(curr_ep->next); ++ if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { ++ /* Disable EP */ ++ next_ep->command &= ~IO_MASK(USB_EP_command, enable); ++ } ++ curr_ep = phys_to_virt(curr_ep->next); ++ } while (curr_ep != first_ep); ++ ++ local_irq_restore(flags); ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ /* Check if EP still is enabled */ ++ if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ tc_warn("sync_finish: Disabling Isoc EP for epid:%d\n", epid); ++ /* The EP was enabled, disable it. */ ++ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ ++ while((*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for DMA-TX-Isoc to leave EP for" ++ " epid:%d\n", epid); ++ } ++ } ++ break; ++ } ++ ++ local_irq_save(flags); ++ ++ /* Finish if there is active URB for this endpoint */ ++ if(activeUrbList[epid] != NULL) { ++ urb = activeUrbList[epid]; ++ urb_priv = urb->hcpriv; ++ ASSERT(urb_priv); ++ tc_warn("Sync finish %s URB:0x%x[%d] (%s %s epid:%d) status:%d %s\n", ++ (urb == activeUrbList[epid]) ? "active" : "queued", ++ (unsigned int)urb, urb_priv->urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), epid, urb->status, ++ (urb_priv->later_data) ? "later-sched" : ""); ++ ++ tc_finish_urb(hcd, activeUrbList[epid], -ENOENT); ++ ASSERT(activeUrbList[epid] == NULL); ++ } ++ ++ /* Finish any queued URBs for this endpoint. There won't be any resubmitions ++ because epid_disabled causes enqueue() to fail for this endpoint */ ++ while((urb = urb_list_first(epid)) != NULL) { ++ urb_priv = urb->hcpriv; ++ ASSERT(urb_priv); ++ ++ tc_warn("Sync finish %s URB:0x%x[%d] (%s %s epid:%d) status:%d %s\n", ++ (urb == activeUrbList[epid]) ? "active" : "queued", ++ (unsigned int)urb, urb_priv->urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), epid, urb->status, ++ (urb_priv->later_data) ? "later-sched" : ""); ++ ++ tc_finish_urb(hcd, urb, -ENOENT); ++ } ++ epid_state[epid].disabled = 0; ++ local_irq_restore(flags); ++} ++ ++/* free resources associated with an endpoint (called from hcd_driver) */ ++static void tc_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) { ++ DBFENTER; ++ /* Only free epid if it has been allocated. We get two endpoint_disable ++ requests for ctrl endpoints so ignore the second one */ ++ if(ep->hcpriv != NULL) { ++ struct crisv10_ep_priv *ep_priv = ep->hcpriv; ++ int epid = ep_priv->epid; ++ tc_warn("endpoint_disable ep:0x%x ep-priv:0x%x (%s) (epid:%d freed)\n", ++ (unsigned int)ep, (unsigned int)ep->hcpriv, ++ endpoint_to_str(&(ep->desc)), epid); ++ ++ tc_sync_finish_epid(hcd, epid); ++ ++ ASSERT(activeUrbList[epid] == NULL); ++ ASSERT(list_empty(&urb_list[epid])); ++ ++ tc_free_epid(ep); ++ } else { ++ tc_dbg("endpoint_disable ep:0x%x ep-priv:0x%x (%s)\n", (unsigned int)ep, ++ (unsigned int)ep->hcpriv, endpoint_to_str(&(ep->desc))); ++ } ++ DBFEXIT; ++} ++ ++//static void tc_finish_urb_later_proc(void *data) { ++static void tc_finish_urb_later_proc(struct work_struct *work) { ++ unsigned long flags; ++ //struct urb_later_data* uld = (struct urb_later_data*)data; ++ struct urb_later_data* uld = container_of(work, struct urb_later_data, ws.work); ++ local_irq_save(flags); ++ if(uld->urb == NULL) { ++ late_dbg("Later finish of URB = NULL (allready finished)\n"); ++ } else { ++ struct crisv10_urb_priv* urb_priv = uld->urb->hcpriv; ++ ASSERT(urb_priv); ++ if(urb_priv->urb_num == uld->urb_num) { ++ late_dbg("Later finish of URB:0x%x[%d]\n", (unsigned int)(uld->urb), ++ urb_priv->urb_num); ++ if(uld->status != uld->urb->status) { ++ errno_dbg("Later-finish URB with status:%d, later-status:%d\n", ++ uld->urb->status, uld->status); ++ } ++ if(uld != urb_priv->later_data) { ++ panic("Scheduled uld not same as URBs uld\n"); ++ } ++ tc_finish_urb(uld->hcd, uld->urb, uld->status); ++ } else { ++ late_warn("Ignoring later finish of URB:0x%x[%d]" ++ ", urb_num doesn't match current URB:0x%x[%d]", ++ (unsigned int)(uld->urb), uld->urb_num, ++ (unsigned int)(uld->urb), urb_priv->urb_num); ++ } ++ } ++ local_irq_restore(flags); ++ kmem_cache_free(later_data_cache, uld); ++} ++ ++static void tc_finish_urb_later(struct usb_hcd *hcd, struct urb *urb, ++ int status) { ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ struct urb_later_data* uld; ++ ++ ASSERT(urb_priv); ++ ++ if(urb_priv->later_data != NULL) { ++ /* Later-finish allready scheduled for this URB, just update status to ++ return when finishing later */ ++ errno_dbg("Later-finish schedule change URB status:%d with new" ++ " status:%d\n", urb_priv->later_data->status, status); ++ ++ urb_priv->later_data->status = status; ++ return; ++ } ++ ++ uld = kmem_cache_alloc(later_data_cache, GFP_ATOMIC); ++ ASSERT(uld); ++ ++ uld->hcd = hcd; ++ uld->urb = urb; ++ uld->urb_num = urb_priv->urb_num; ++ uld->status = status; ++ ++ //INIT_WORK(&uld->ws, tc_finish_urb_later_proc, uld); ++ INIT_DELAYED_WORK(&uld->ws, tc_finish_urb_later_proc); ++ urb_priv->later_data = uld; ++ ++ /* Schedule the finishing of the URB to happen later */ ++ schedule_delayed_work(&uld->ws, LATER_TIMER_DELAY); ++} ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++static void tc_finish_isoc_urb(struct usb_hcd *hcd, struct urb *urb, ++ int status); ++#endif ++ ++static void tc_finish_urb(struct usb_hcd *hcd, struct urb *urb, int status) { ++ struct crisv10_hcd* crisv10_hcd = hcd_to_crisv10_hcd(hcd); ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ int epid; ++ char toggle; ++ int urb_num; ++ ++ DBFENTER; ++ ASSERT(urb_priv != NULL); ++ epid = urb_priv->epid; ++ urb_num = urb_priv->urb_num; ++ ++ if(urb != activeUrbList[epid]) { ++ if(urb_list_entry(urb, epid)) { ++ /* Remove this URB from the list. Only happens when URB are finished ++ before having been processed (dequeing) */ ++ urb_list_del(urb, epid); ++ } else { ++ tc_warn("Finishing of URB:0x%x[%d] neither active or in queue for" ++ " epid:%d\n", (unsigned int)urb, urb_num, epid); ++ } ++ } ++ ++ /* Cancel any pending later-finish of this URB */ ++ if(urb_priv->later_data) { ++ urb_priv->later_data->urb = NULL; ++ } ++ ++ /* For an IN pipe, we always set the actual length, regardless of whether ++ there was an error or not (which means the device driver can use the data ++ if it wants to). */ ++ if(usb_pipein(urb->pipe)) { ++ urb->actual_length = urb_priv->rx_offset; ++ } else { ++ /* Set actual_length for OUT urbs also; the USB mass storage driver seems ++ to want that. */ ++ if (status == 0 && urb->status == -EINPROGRESS) { ++ urb->actual_length = urb->transfer_buffer_length; ++ } else { ++ /* We wouldn't know of any partial writes if there was an error. */ ++ urb->actual_length = 0; ++ } ++ } ++ ++ ++ /* URB status mangling */ ++ if(urb->status == -EINPROGRESS) { ++ /* The USB core hasn't changed the status, let's set our finish status */ ++ urb->status = status; ++ ++ if ((status == 0) && (urb->transfer_flags & URB_SHORT_NOT_OK) && ++ usb_pipein(urb->pipe) && ++ (urb->actual_length != urb->transfer_buffer_length)) { ++ /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's ++ max length) is to be treated as an error. */ ++ errno_dbg("Finishing URB:0x%x[%d] with SHORT_NOT_OK flag and short" ++ " data:%d\n", (unsigned int)urb, urb_num, ++ urb->actual_length); ++ urb->status = -EREMOTEIO; ++ } ++ ++ if(urb_priv->urb_state == UNLINK) { ++ /* URB has been requested to be unlinked asynchronously */ ++ urb->status = -ECONNRESET; ++ errno_dbg("Fixing unlink status of URB:0x%x[%d] to:%d\n", ++ (unsigned int)urb, urb_num, urb->status); ++ } ++ } else { ++ /* The USB Core wants to signal some error via the URB, pass it through */ ++ } ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++ /* use completely different finish function for Isoc URBs */ ++ if(usb_pipeisoc(urb->pipe)) { ++ tc_finish_isoc_urb(hcd, urb, status); ++ return; ++ } ++#endif ++ ++ /* Do special unlinking of EPs for Intr traffic */ ++ if(usb_pipeint(urb->pipe)) { ++ tc_dma_unlink_intr_urb(urb); ++ } ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++ /* Release allocated bandwidth for periodic transfers */ ++ if(usb_pipeint(urb->pipe) || usb_pipeisoc(urb->pipe)) ++ usb_release_bandwidth(urb->dev, urb, 0); ++#endif ++ ++ /* This URB is active on EP */ ++ if(urb == activeUrbList[epid]) { ++ /* We need to fiddle with the toggle bits because the hardware doesn't do ++ it for us. */ ++ toggle = etrax_epid_get_toggle(epid, usb_pipeout(urb->pipe)); ++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), ++ usb_pipeout(urb->pipe), toggle); ++ ++ /* Checks for Ctrl and Bulk EPs */ ++ switch(usb_pipetype(urb->pipe)) { ++ case PIPE_BULK: ++ /* Check so Bulk EP realy is disabled before finishing active URB */ ++ ASSERT((TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) == ++ IO_STATE(USB_EP_command, enable, no)); ++ /* Disable sub-pointer for EP to avoid next tx_interrupt() to ++ process Bulk EP. */ ++ TxBulkEPList[epid].sub = 0; ++ /* No need to wait for the DMA before changing the next pointer. ++ The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use ++ the last one (INVALID_EPID) for actual traffic. */ ++ TxBulkEPList[epid].next = ++ virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); ++ break; ++ case PIPE_CONTROL: ++ /* Check so Ctrl EP realy is disabled before finishing active URB */ ++ ASSERT((TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) == ++ IO_STATE(USB_EP_command, enable, no)); ++ /* Disable sub-pointer for EP to avoid next tx_interrupt() to ++ process Ctrl EP. */ ++ TxCtrlEPList[epid].sub = 0; ++ break; ++ } ++ } ++ ++ /* Free HC-private URB data*/ ++ urb_priv_free(hcd, urb); ++ ++ if(urb->status) { ++ errno_dbg("finish_urb (URB:0x%x[%d] %s %s) (data:%d) status:%d\n", ++ (unsigned int)urb, urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), urb->actual_length, urb->status); ++ } else { ++ tc_dbg("finish_urb (URB:0x%x[%d] %s %s) (data:%d) status:%d\n", ++ (unsigned int)urb, urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), urb->actual_length, urb->status); ++ } ++ ++ /* If we just finished an active URB, clear active pointer. */ ++ if (urb == activeUrbList[epid]) { ++ /* Make URB not active on EP anymore */ ++ activeUrbList[epid] = NULL; ++ ++ if(urb->status == 0) { ++ /* URB finished sucessfully, process queue to see if there are any more ++ URBs waiting before we call completion function.*/ ++ if(crisv10_hcd->running) { ++ /* Only process queue if USB controller is running */ ++ tc_dma_process_queue(epid); ++ } else { ++ tc_warn("No processing of queue for epid:%d, USB Controller not" ++ " running\n", epid); ++ } ++ } ++ } ++ ++ /* Hand the URB from HCD to its USB device driver, using its completion ++ functions */ ++// usb_hcd_giveback_urb (hcd, urb); ++ /** ++ * usb_hcd_unlink_urb_from_ep - remove an URB from its endpoint queue ++ * @hcd: host controller to which @urb was submitted ++ * @urb: URB being unlinked ++ * ++ * Host controller drivers should call this routine before calling ++ * usb_hcd_giveback_urb(). The HCD's private spinlock must be held and ++ * interrupts must be disabled. The actions carried out here are required ++ * for URB completion. ++ */ ++ ++ /*hinko link/unlink urb -> ep */ ++ //spin_lock(&crisv10_hcd->lock); ++ unsigned long flags; ++ spin_lock_irqsave(&crisv10_hcd->lock, flags); ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++ usb_hcd_giveback_urb(hcd, urb, status); ++ //spin_unlock(&crisv10_hcd->lock); ++ spin_unlock_irqrestore(&crisv10_hcd->lock, flags); ++ ++ /* Check the queue once more if the URB returned with error, because we ++ didn't do it before the completion function because the specification ++ states that the queue should not restart until all it's unlinked ++ URBs have been fully retired, with the completion functions run */ ++ if(crisv10_hcd->running) { ++ /* Only process queue if USB controller is running */ ++ tc_dma_process_queue(epid); ++ } else { ++ tc_warn("No processing of queue for epid:%d, USB Controller not running\n", ++ epid); ++ } ++ ++ DBFEXIT; ++} ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++static void tc_finish_isoc_urb(struct usb_hcd *hcd, struct urb *urb, ++ int status) { ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ int epid, i; ++ volatile int timeout = 10000; ++ ++ ASSERT(urb_priv); ++ epid = urb_priv->epid; ++ ++ ASSERT(usb_pipeisoc(urb->pipe)); ++ ++ /* Set that all isoc packets have status and length set before ++ completing the urb. */ ++ for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++){ ++ urb->iso_frame_desc[i].actual_length = 0; ++ urb->iso_frame_desc[i].status = -EPROTO; ++ } ++ ++ /* Check if the URB is currently active (done or error) */ ++ if(urb == activeUrbList[epid]) { ++ /* Check if there are another In Isoc URB queued for this epid */ ++ if (!list_empty(&urb_list[epid])&& !epid_state[epid].disabled) { ++ /* Move it from queue to active and mark it started so Isoc transfers ++ won't be interrupted. ++ All Isoc URBs data transfers are already added to DMA lists so we ++ don't have to insert anything in DMA lists here. */ ++ activeUrbList[epid] = urb_list_first(epid); ++ ((struct crisv10_urb_priv *)(activeUrbList[epid]->hcpriv))->urb_state = ++ STARTED; ++ urb_list_del(activeUrbList[epid], epid); ++ ++ if(urb->status) { ++ errno_dbg("finish_isoc_urb (URB:0x%x[%d] %s %s) (%d of %d packets)" ++ " status:%d, new waiting URB:0x%x[%d]\n", ++ (unsigned int)urb, urb_priv->urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), urb_priv->isoc_packet_counter, ++ urb->number_of_packets, urb->status, ++ (unsigned int)activeUrbList[epid], ++ ((struct crisv10_urb_priv *)(activeUrbList[epid]->hcpriv))->urb_num); ++ } ++ ++ } else { /* No other URB queued for this epid */ ++ if(urb->status) { ++ errno_dbg("finish_isoc_urb (URB:0x%x[%d] %s %s) (%d of %d packets)" ++ " status:%d, no new URB waiting\n", ++ (unsigned int)urb, urb_priv->urb_num, str_dir(urb->pipe), ++ str_type(urb->pipe), urb_priv->isoc_packet_counter, ++ urb->number_of_packets, urb->status); ++ } ++ ++ /* Check if EP is still enabled, then shut it down. */ ++ if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ isoc_dbg("Isoc EP enabled for epid:%d, disabling it\n", epid); ++ ++ /* Should only occur for In Isoc EPs where SB isn't consumed. */ ++ ASSERT(usb_pipein(urb->pipe)); ++ ++ /* Disable it and wait for it to stop */ ++ TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); ++ ++ /* Ah, the luxury of busy-wait. */ ++ while((*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for DMA-TX-Isoc to leave EP for epid:%d\n", epid); ++ } ++ } ++ ++ /* Unlink SB to say that epid is finished. */ ++ TxIsocEPList[epid].sub = 0; ++ TxIsocEPList[epid].hw_len = 0; ++ ++ /* No URB active for EP anymore */ ++ activeUrbList[epid] = NULL; ++ } ++ } else { /* Finishing of not active URB (queued up with SBs thought) */ ++ isoc_warn("finish_isoc_urb (URB:0x%x %s) (%d of %d packets) status:%d," ++ " SB queued but not active\n", ++ (unsigned int)urb, str_dir(urb->pipe), ++ urb_priv->isoc_packet_counter, urb->number_of_packets, ++ urb->status); ++ if(usb_pipeout(urb->pipe)) { ++ /* Finishing of not yet active Out Isoc URB needs unlinking of SBs. */ ++ struct USB_SB_Desc *iter_sb, *prev_sb, *next_sb; ++ ++ iter_sb = TxIsocEPList[epid].sub ? ++ phys_to_virt(TxIsocEPList[epid].sub) : 0; ++ prev_sb = 0; ++ ++ /* SB that is linked before this URBs first SB */ ++ while (iter_sb && (iter_sb != urb_priv->first_sb)) { ++ prev_sb = iter_sb; ++ iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; ++ } ++ ++ if (iter_sb == 0) { ++ /* Unlink of the URB currently being transmitted. */ ++ prev_sb = 0; ++ iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; ++ } ++ ++ while (iter_sb && (iter_sb != urb_priv->last_sb)) { ++ iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; ++ } ++ ++ if (iter_sb) { ++ next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; ++ } else { ++ /* This should only happen if the DMA has completed ++ processing the SB list for this EP while interrupts ++ are disabled. */ ++ isoc_dbg("Isoc urb not found, already sent?\n"); ++ next_sb = 0; ++ } ++ if (prev_sb) { ++ prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0; ++ } else { ++ TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0; ++ } ++ } ++ } ++ ++ /* Free HC-private URB data*/ ++ urb_priv_free(hcd, urb); ++ ++ usb_release_bandwidth(urb->dev, urb, 0); ++ ++ /* Hand the URB from HCD to its USB device driver, using its completion ++ functions */ ++ usb_hcd_giveback_urb (hcd, urb); ++} ++#endif ++ ++static __u32 urb_num = 0; ++ ++/* allocate and initialize URB private data */ ++static int urb_priv_create(struct usb_hcd *hcd, struct urb *urb, int epid, ++ int mem_flags) { ++ struct crisv10_urb_priv *urb_priv; ++ ++ urb_priv = kmalloc(sizeof *urb_priv, mem_flags); ++ if (!urb_priv) ++ return -ENOMEM; ++ memset(urb_priv, 0, sizeof *urb_priv); ++ ++ urb_priv->epid = epid; ++ urb_priv->urb_state = NOT_STARTED; ++ ++ urb->hcpriv = urb_priv; ++ /* Assign URB a sequence number, and increment counter */ ++ urb_priv->urb_num = urb_num; ++ urb_num++; ++ return 0; ++} ++ ++/* free URB private data */ ++static void urb_priv_free(struct usb_hcd *hcd, struct urb *urb) { ++ int i; ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ ASSERT(urb_priv != 0); ++ ++ /* Check it has any SBs linked that needs to be freed*/ ++ if(urb_priv->first_sb != NULL) { ++ struct USB_SB_Desc *next_sb, *first_sb, *last_sb; ++ int i = 0; ++ first_sb = urb_priv->first_sb; ++ last_sb = urb_priv->last_sb; ++ ASSERT(last_sb); ++ while(first_sb != last_sb) { ++ next_sb = (struct USB_SB_Desc *)phys_to_virt(first_sb->next); ++ kmem_cache_free(usb_desc_cache, first_sb); ++ first_sb = next_sb; ++ i++; ++ } ++ kmem_cache_free(usb_desc_cache, last_sb); ++ i++; ++ } ++ ++ /* Check if it has any EPs in its Intr pool that also needs to be freed */ ++ if(urb_priv->intr_ep_pool_length > 0) { ++ for(i = 0; i < urb_priv->intr_ep_pool_length; i++) { ++ kfree(urb_priv->intr_ep_pool[i]); ++ } ++ /* ++ tc_dbg("Freed %d EPs from URB:0x%x EP pool\n", ++ urb_priv->intr_ep_pool_length, (unsigned int)urb); ++ */ ++ } ++ ++ kfree(urb_priv); ++ urb->hcpriv = NULL; ++} ++ ++static int ep_priv_create(struct usb_host_endpoint *ep, int mem_flags) { ++ struct crisv10_ep_priv *ep_priv; ++ ++ ep_priv = kmalloc(sizeof *ep_priv, mem_flags); ++ if (!ep_priv) ++ return -ENOMEM; ++ memset(ep_priv, 0, sizeof *ep_priv); ++ ++ ep->hcpriv = ep_priv; ++ return 0; ++} ++ ++static void ep_priv_free(struct usb_host_endpoint *ep) { ++ struct crisv10_ep_priv *ep_priv = ep->hcpriv; ++ ASSERT(ep_priv); ++ kfree(ep_priv); ++ ep->hcpriv = NULL; ++} ++ ++/* EPID handling functions, managing EP-list in Etrax through wrappers */ ++/* ------------------------------------------------------------------- */ ++ ++/* Sets up a new EPID for an endpoint or returns existing if found */ ++//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) ++{ ++ int epid; ++ char devnum, endpoint, out_traffic, slow; ++ int maxlen; ++ __u32 epid_data; ++ struct usb_host_endpoint *ep = urb->ep; ++ struct crisv10_ep_priv *ep_priv = ep->hcpriv; ++ ++ DBFENTER; ++ ++ /* Check if a valid epid already is setup for this endpoint */ ++ if(ep_priv != NULL) { ++ return ep_priv->epid; ++ } ++ ++ /* We must find and initiate a new epid for this urb. */ ++ epid = tc_allocate_epid(); ++ ++ if (epid == -1) { ++ /* Failed to allocate a new epid. */ ++ DBFEXIT; ++ return epid; ++ } ++ ++ /* We now have a new epid to use. Claim it. */ ++ epid_state[epid].inuse = 1; ++ ++ /* Init private data for new endpoint */ ++ if(ep_priv_create(ep, mem_flags) != 0) { ++ return -ENOMEM; ++ } ++ ep_priv = ep->hcpriv; ++ ep_priv->epid = epid; ++ ++ devnum = usb_pipedevice(urb->pipe); ++ endpoint = usb_pipeendpoint(urb->pipe); ++ slow = (urb->dev->speed == USB_SPEED_LOW); ++ maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); ++ ++ if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { ++ /* We want both IN and OUT control traffic to be put on the same ++ EP/SB list. */ ++ out_traffic = 1; ++ } else { ++ out_traffic = usb_pipeout(urb->pipe); ++ } ++ ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ epid_data = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) | ++ /* FIXME: Change any to the actual port? */ ++ IO_STATE(R_USB_EPT_DATA_ISO, port, any) | ++ IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) | ++ IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) | ++ IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum); ++ etrax_epid_iso_set(epid, epid_data); ++ } else { ++ epid_data = IO_STATE(R_USB_EPT_DATA, valid, yes) | ++ IO_FIELD(R_USB_EPT_DATA, low_speed, slow) | ++ /* FIXME: Change any to the actual port? */ ++ IO_STATE(R_USB_EPT_DATA, port, any) | ++ IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) | ++ IO_FIELD(R_USB_EPT_DATA, ep, endpoint) | ++ IO_FIELD(R_USB_EPT_DATA, dev, devnum); ++ etrax_epid_set(epid, epid_data); ++ } ++ ++ epid_state[epid].out_traffic = out_traffic; ++ epid_state[epid].type = usb_pipetype(urb->pipe); ++ ++ tc_warn("Setting up ep:0x%x epid:%d (addr:%d endp:%d max_len:%d %s %s %s)\n", ++ (unsigned int)ep, epid, devnum, endpoint, maxlen, ++ str_type(urb->pipe), out_traffic ? "out" : "in", ++ slow ? "low" : "full"); ++ ++ /* Enable Isoc eof interrupt if we set up the first Isoc epid */ ++ if(usb_pipeisoc(urb->pipe)) { ++ isoc_epid_counter++; ++ if(isoc_epid_counter == 1) { ++ isoc_warn("Enabled Isoc eof interrupt\n"); ++ *R_USB_IRQ_MASK_SET |= IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set); ++ } ++ } ++ ++ DBFEXIT; ++ return epid; ++} ++ ++static void tc_free_epid(struct usb_host_endpoint *ep) { ++ unsigned long flags; ++ struct crisv10_ep_priv *ep_priv = ep->hcpriv; ++ int epid; ++ volatile int timeout = 10000; ++ ++ DBFENTER; ++ ++ if (ep_priv == NULL) { ++ tc_warn("Trying to free unused epid on ep:0x%x\n", (unsigned int)ep); ++ DBFEXIT; ++ return; ++ } ++ ++ epid = ep_priv->epid; ++ ++ /* Disable Isoc eof interrupt if we free the last Isoc epid */ ++ if(epid_isoc(epid)) { ++ ASSERT(isoc_epid_counter > 0); ++ isoc_epid_counter--; ++ if(isoc_epid_counter == 0) { ++ *R_USB_IRQ_MASK_SET &= ~IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set); ++ isoc_warn("Disabled Isoc eof interrupt\n"); ++ } ++ } ++ ++ /* Take lock manualy instead of in epid_x_x wrappers, ++ because we need to be polling here */ ++ spin_lock_irqsave(&etrax_epid_lock, flags); ++ ++ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); ++ nop(); ++ while((*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for epid:%d to drop hold\n", epid); ++ } ++ /* This will, among other things, set the valid field to 0. */ ++ *R_USB_EPT_DATA = 0; ++ spin_unlock_irqrestore(&etrax_epid_lock, flags); ++ ++ /* Free resource in software state info list */ ++ epid_state[epid].inuse = 0; ++ ++ /* Free private endpoint data */ ++ ep_priv_free(ep); ++ ++ DBFEXIT; ++} ++ ++static int tc_allocate_epid(void) { ++ int i; ++ DBFENTER; ++ for (i = 0; i < NBR_OF_EPIDS; i++) { ++ if (!epid_inuse(i)) { ++ DBFEXIT; ++ return i; ++ } ++ } ++ ++ tc_warn("Found no free epids\n"); ++ DBFEXIT; ++ return -1; ++} ++ ++ ++/* Wrappers around the list functions (include/linux/list.h). */ ++/* ---------------------------------------------------------- */ ++static inline int __urb_list_empty(int epid) { ++ int retval; ++ retval = list_empty(&urb_list[epid]); ++ return retval; ++} ++ ++/* Returns first urb for this epid, or NULL if list is empty. */ ++static inline struct urb *urb_list_first(int epid) { ++ unsigned long flags; ++ struct urb *first_urb = 0; ++ spin_lock_irqsave(&urb_list_lock, flags); ++ if (!__urb_list_empty(epid)) { ++ /* Get the first urb (i.e. head->next). */ ++ urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list); ++ first_urb = urb_entry->urb; ++ } ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++ return first_urb; ++} ++ ++/* Adds an urb_entry last in the list for this epid. */ ++static inline void urb_list_add(struct urb *urb, int epid, int mem_flags) { ++ unsigned long flags; ++ urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), mem_flags); ++ ASSERT(urb_entry); ++ ++ urb_entry->urb = urb; ++ spin_lock_irqsave(&urb_list_lock, flags); ++ list_add_tail(&urb_entry->list, &urb_list[epid]); ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++} ++ ++/* Search through the list for an element that contains this urb. (The list ++ is expected to be short and the one we are about to delete will often be ++ the first in the list.) ++ Should be protected by spin_locks in calling function */ ++static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid) { ++ struct list_head *entry; ++ struct list_head *tmp; ++ urb_entry_t *urb_entry; ++ ++ list_for_each_safe(entry, tmp, &urb_list[epid]) { ++ urb_entry = list_entry(entry, urb_entry_t, list); ++ ASSERT(urb_entry); ++ ASSERT(urb_entry->urb); ++ ++ if (urb_entry->urb == urb) { ++ return urb_entry; ++ } ++ } ++ return 0; ++} ++ ++/* Same function as above but for global use. Protects list by spinlock */ ++static inline urb_entry_t *urb_list_entry(struct urb *urb, int epid) { ++ unsigned long flags; ++ urb_entry_t *urb_entry; ++ spin_lock_irqsave(&urb_list_lock, flags); ++ urb_entry = __urb_list_entry(urb, epid); ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++ return (urb_entry); ++} ++ ++/* Delete an urb from the list. */ ++static inline void urb_list_del(struct urb *urb, int epid) { ++ unsigned long flags; ++ urb_entry_t *urb_entry; ++ ++ /* Delete entry and free. */ ++ spin_lock_irqsave(&urb_list_lock, flags); ++ urb_entry = __urb_list_entry(urb, epid); ++ ASSERT(urb_entry); ++ ++ list_del(&urb_entry->list); ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++ kfree(urb_entry); ++} ++ ++/* Move an urb to the end of the list. */ ++static inline void urb_list_move_last(struct urb *urb, int epid) { ++ unsigned long flags; ++ urb_entry_t *urb_entry; ++ ++ spin_lock_irqsave(&urb_list_lock, flags); ++ urb_entry = __urb_list_entry(urb, epid); ++ ASSERT(urb_entry); ++ ++ list_del(&urb_entry->list); ++ list_add_tail(&urb_entry->list, &urb_list[epid]); ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++} ++ ++/* Get the next urb in the list. */ ++static inline struct urb *urb_list_next(struct urb *urb, int epid) { ++ unsigned long flags; ++ urb_entry_t *urb_entry; ++ ++ spin_lock_irqsave(&urb_list_lock, flags); ++ urb_entry = __urb_list_entry(urb, epid); ++ ASSERT(urb_entry); ++ ++ if (urb_entry->list.next != &urb_list[epid]) { ++ struct list_head *elem = urb_entry->list.next; ++ urb_entry = list_entry(elem, urb_entry_t, list); ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++ return urb_entry->urb; ++ } else { ++ spin_unlock_irqrestore(&urb_list_lock, flags); ++ return NULL; ++ } ++} ++ ++struct USB_EP_Desc* create_ep(int epid, struct USB_SB_Desc* sb_desc, ++ int mem_flags) { ++ struct USB_EP_Desc *ep_desc; ++ ep_desc = (struct USB_EP_Desc *) kmem_cache_alloc(usb_desc_cache, mem_flags); ++ if(ep_desc == NULL) ++ return NULL; ++ memset(ep_desc, 0, sizeof(struct USB_EP_Desc)); ++ ++ ep_desc->hw_len = 0; ++ ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) | ++ IO_STATE(USB_EP_command, enable, yes)); ++ if(sb_desc == NULL) { ++ ep_desc->sub = 0; ++ } else { ++ ep_desc->sub = virt_to_phys(sb_desc); ++ } ++ return ep_desc; ++} ++ ++#define TT_ZOUT 0 ++#define TT_IN 1 ++#define TT_OUT 2 ++#define TT_SETUP 3 ++ ++#define CMD_EOL IO_STATE(USB_SB_command, eol, yes) ++#define CMD_INTR IO_STATE(USB_SB_command, intr, yes) ++#define CMD_FULL IO_STATE(USB_SB_command, full, yes) ++ ++/* Allocation and setup of a generic SB. Used to create SETUP, OUT and ZOUT ++ SBs. Also used by create_sb_in() to avoid same allocation procedure at two ++ places */ ++struct USB_SB_Desc* create_sb(struct USB_SB_Desc* sb_prev, int tt, void* data, ++ int datalen, int mem_flags) { ++ struct USB_SB_Desc *sb_desc; ++ sb_desc = (struct USB_SB_Desc*)kmem_cache_alloc(usb_desc_cache, mem_flags); ++ if(sb_desc == NULL) ++ return NULL; ++ memset(sb_desc, 0, sizeof(struct USB_SB_Desc)); ++ ++ sb_desc->command = IO_FIELD(USB_SB_command, tt, tt) | ++ IO_STATE(USB_SB_command, eot, yes); ++ ++ sb_desc->sw_len = datalen; ++ if(data != NULL) { ++ sb_desc->buf = virt_to_phys(data); ++ } else { ++ sb_desc->buf = 0; ++ } ++ if(sb_prev != NULL) { ++ sb_prev->next = virt_to_phys(sb_desc); ++ } ++ return sb_desc; ++} ++ ++/* Creates a copy of an existing SB by allocation space for it and copy ++ settings */ ++struct USB_SB_Desc* create_sb_copy(struct USB_SB_Desc* sb_orig, int mem_flags) { ++ struct USB_SB_Desc *sb_desc; ++ sb_desc = (struct USB_SB_Desc*)kmem_cache_alloc(usb_desc_cache, mem_flags); ++ if(sb_desc == NULL) ++ return NULL; ++ ++ memcpy(sb_desc, sb_orig, sizeof(struct USB_SB_Desc)); ++ return sb_desc; ++} ++ ++/* A specific create_sb function for creation of in SBs. This is due to ++ that datalen in In SBs shows how many packets we are expecting. It also ++ sets up the rem field to show if how many bytes we expect in last packet ++ if it's not a full one */ ++struct USB_SB_Desc* create_sb_in(struct USB_SB_Desc* sb_prev, int datalen, ++ int maxlen, int mem_flags) { ++ struct USB_SB_Desc *sb_desc; ++ sb_desc = create_sb(sb_prev, TT_IN, NULL, ++ datalen ? (datalen - 1) / maxlen + 1 : 0, mem_flags); ++ if(sb_desc == NULL) ++ return NULL; ++ sb_desc->command |= IO_FIELD(USB_SB_command, rem, datalen % maxlen); ++ return sb_desc; ++} ++ ++void set_sb_cmds(struct USB_SB_Desc *sb_desc, __u16 flags) { ++ sb_desc->command |= flags; ++} ++ ++int create_sb_for_urb(struct urb *urb, int mem_flags) { ++ int is_out = !usb_pipein(urb->pipe); ++ int type = usb_pipetype(urb->pipe); ++ int maxlen = usb_maxpacket(urb->dev, urb->pipe, is_out); ++ int buf_len = urb->transfer_buffer_length; ++ void *buf = buf_len > 0 ? urb->transfer_buffer : NULL; ++ struct USB_SB_Desc *sb_desc = NULL; ++ ++ struct crisv10_urb_priv *urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv != NULL); ++ ++ switch(type) { ++ case PIPE_CONTROL: ++ /* Setup stage */ ++ sb_desc = create_sb(NULL, TT_SETUP, urb->setup_packet, 8, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ set_sb_cmds(sb_desc, CMD_FULL); ++ ++ /* Attach first SB to URB */ ++ urb_priv->first_sb = sb_desc; ++ ++ if (is_out) { /* Out Control URB */ ++ /* If this Control OUT transfer has an optional data stage we add ++ an OUT token before the mandatory IN (status) token */ ++ if ((buf_len > 0) && buf) { ++ sb_desc = create_sb(sb_desc, TT_OUT, buf, buf_len, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ set_sb_cmds(sb_desc, CMD_FULL); ++ } ++ ++ /* Status stage */ ++ /* The data length has to be exactly 1. This is due to a requirement ++ of the USB specification that a host must be prepared to receive ++ data in the status phase */ ++ sb_desc = create_sb(sb_desc, TT_IN, NULL, 1, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ } else { /* In control URB */ ++ /* Data stage */ ++ sb_desc = create_sb_in(sb_desc, buf_len, maxlen, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ ++ /* Status stage */ ++ /* Read comment at zout_buffer declaration for an explanation to this. */ ++ sb_desc = create_sb(sb_desc, TT_ZOUT, &zout_buffer[0], 1, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ /* Set descriptor interrupt flag for in URBs so we can finish URB after ++ zout-packet has been sent */ ++ set_sb_cmds(sb_desc, CMD_INTR | CMD_FULL); ++ } ++ /* Set end-of-list flag in last SB */ ++ set_sb_cmds(sb_desc, CMD_EOL); ++ /* Attach last SB to URB */ ++ urb_priv->last_sb = sb_desc; ++ break; ++ ++ case PIPE_BULK: ++ if (is_out) { /* Out Bulk URB */ ++ sb_desc = create_sb(NULL, TT_OUT, buf, buf_len, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ /* The full field is set to yes, even if we don't actually check that ++ this is a full-length transfer (i.e., that transfer_buffer_length % ++ maxlen = 0). ++ Setting full prevents the USB controller from sending an empty packet ++ in that case. However, if URB_ZERO_PACKET was set we want that. */ ++ if (!(urb->transfer_flags & URB_ZERO_PACKET)) { ++ set_sb_cmds(sb_desc, CMD_FULL); ++ } ++ } else { /* In Bulk URB */ ++ sb_desc = create_sb_in(NULL, buf_len, maxlen, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ } ++ /* Set end-of-list flag for last SB */ ++ set_sb_cmds(sb_desc, CMD_EOL); ++ ++ /* Attach SB to URB */ ++ urb_priv->first_sb = sb_desc; ++ urb_priv->last_sb = sb_desc; ++ break; ++ ++ case PIPE_INTERRUPT: ++ if(is_out) { /* Out Intr URB */ ++ sb_desc = create_sb(NULL, TT_OUT, buf, buf_len, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ ++ /* The full field is set to yes, even if we don't actually check that ++ this is a full-length transfer (i.e., that transfer_buffer_length % ++ maxlen = 0). ++ Setting full prevents the USB controller from sending an empty packet ++ in that case. However, if URB_ZERO_PACKET was set we want that. */ ++ if (!(urb->transfer_flags & URB_ZERO_PACKET)) { ++ set_sb_cmds(sb_desc, CMD_FULL); ++ } ++ /* Only generate TX interrupt if it's a Out URB*/ ++ set_sb_cmds(sb_desc, CMD_INTR); ++ ++ } else { /* In Intr URB */ ++ sb_desc = create_sb_in(NULL, buf_len, maxlen, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ } ++ /* Set end-of-list flag for last SB */ ++ set_sb_cmds(sb_desc, CMD_EOL); ++ ++ /* Attach SB to URB */ ++ urb_priv->first_sb = sb_desc; ++ urb_priv->last_sb = sb_desc; ++ ++ break; ++ case PIPE_ISOCHRONOUS: ++ if(is_out) { /* Out Isoc URB */ ++ int i; ++ if(urb->number_of_packets == 0) { ++ tc_err("Can't create SBs for Isoc URB with zero packets\n"); ++ return -EPIPE; ++ } ++ /* Create one SB descriptor for each packet and link them together. */ ++ for(i = 0; i < urb->number_of_packets; i++) { ++ if (urb->iso_frame_desc[i].length > 0) { ++ ++ sb_desc = create_sb(sb_desc, TT_OUT, urb->transfer_buffer + ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].length, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ ++ /* Check if it's a full length packet */ ++ if (urb->iso_frame_desc[i].length == ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { ++ set_sb_cmds(sb_desc, CMD_FULL); ++ } ++ ++ } else { /* zero length packet */ ++ sb_desc = create_sb(sb_desc, TT_ZOUT, &zout_buffer[0], 1, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ set_sb_cmds(sb_desc, CMD_FULL); ++ } ++ /* Attach first SB descriptor to URB */ ++ if (i == 0) { ++ urb_priv->first_sb = sb_desc; ++ } ++ } ++ /* Set interrupt and end-of-list flags in last SB */ ++ set_sb_cmds(sb_desc, CMD_INTR | CMD_EOL); ++ /* Attach last SB descriptor to URB */ ++ urb_priv->last_sb = sb_desc; ++ tc_dbg("Created %d out SBs for Isoc URB:0x%x\n", ++ urb->number_of_packets, (unsigned int)urb); ++ } else { /* In Isoc URB */ ++ /* Actual number of packets is not relevant for periodic in traffic as ++ long as it is more than zero. Set to 1 always. */ ++ sb_desc = create_sb(sb_desc, TT_IN, NULL, 1, mem_flags); ++ if(sb_desc == NULL) ++ return -ENOMEM; ++ /* Set end-of-list flags for SB */ ++ set_sb_cmds(sb_desc, CMD_EOL); ++ ++ /* Attach SB to URB */ ++ urb_priv->first_sb = sb_desc; ++ urb_priv->last_sb = sb_desc; ++ } ++ break; ++ default: ++ tc_err("Unknown pipe-type\n"); ++ return -EPIPE; ++ break; ++ } ++ return 0; ++} ++ ++int init_intr_urb(struct urb *urb, int mem_flags) { ++ struct crisv10_urb_priv *urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ struct USB_EP_Desc* ep_desc; ++ int interval; ++ int i; ++ int ep_count; ++ ++ ASSERT(urb_priv != NULL); ++ ASSERT(usb_pipeint(urb->pipe)); ++ /* We can't support interval longer than amount of eof descriptors in ++ TxIntrEPList */ ++ if(urb->interval > MAX_INTR_INTERVAL) { ++ tc_err("Interrupt interval %dms too big (max: %dms)\n", urb->interval, ++ MAX_INTR_INTERVAL); ++ return -EINVAL; ++ } ++ ++ /* We assume that the SB descriptors already have been setup */ ++ ASSERT(urb_priv->first_sb != NULL); ++ ++ /* Round of the interval to 2^n, it is obvious that this code favours ++ smaller numbers, but that is actually a good thing */ ++ /* FIXME: The "rounding error" for larger intervals will be quite ++ large. For in traffic this shouldn't be a problem since it will only ++ mean that we "poll" more often. */ ++ interval = urb->interval; ++ for (i = 0; interval; i++) { ++ interval = interval >> 1; ++ } ++ urb_priv->interval = 1 << (i - 1); ++ ++ /* We can only have max interval for Out Interrupt due to that we can only ++ handle one linked in EP for a certain epid in the Intr descr array at the ++ time. The USB Controller in the Etrax 100LX continues to process Intr EPs ++ so we have no way of knowing which one that caused the actual transfer if ++ we have several linked in. */ ++ if(usb_pipeout(urb->pipe)) { ++ urb_priv->interval = MAX_INTR_INTERVAL; ++ } ++ ++ /* Calculate amount of EPs needed */ ++ ep_count = MAX_INTR_INTERVAL / urb_priv->interval; ++ ++ for(i = 0; i < ep_count; i++) { ++ ep_desc = create_ep(urb_priv->epid, urb_priv->first_sb, mem_flags); ++ if(ep_desc == NULL) { ++ /* Free any descriptors that we may have allocated before failure */ ++ while(i > 0) { ++ i--; ++ kfree(urb_priv->intr_ep_pool[i]); ++ } ++ return -ENOMEM; ++ } ++ urb_priv->intr_ep_pool[i] = ep_desc; ++ } ++ urb_priv->intr_ep_pool_length = ep_count; ++ return 0; ++} ++ ++/* DMA RX/TX functions */ ++/* ----------------------- */ ++ ++static void tc_dma_init_rx_list(void) { ++ int i; ++ ++ /* Setup descriptor list except last one */ ++ for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { ++ RxDescList[i].sw_len = RX_DESC_BUF_SIZE; ++ RxDescList[i].command = 0; ++ RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); ++ RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); ++ RxDescList[i].hw_len = 0; ++ RxDescList[i].status = 0; ++ ++ /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as ++ USB_IN_Desc for the relevant fields.) */ ++ prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]); ++ ++ } ++ /* Special handling of last descriptor */ ++ RxDescList[i].sw_len = RX_DESC_BUF_SIZE; ++ RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes); ++ RxDescList[i].next = virt_to_phys(&RxDescList[0]); ++ RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); ++ RxDescList[i].hw_len = 0; ++ RxDescList[i].status = 0; ++ ++ /* Setup list pointers that show progress in list */ ++ myNextRxDesc = &RxDescList[0]; ++ myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; ++ ++ flush_etrax_cache(); ++ /* Point DMA to first descriptor in list and start it */ ++ *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc); ++ *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start); ++} ++ ++ ++static void tc_dma_init_tx_bulk_list(void) { ++ int i; ++ volatile struct USB_EP_Desc *epDescr; ++ ++ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { ++ epDescr = &(TxBulkEPList[i]); ++ CHECK_ALIGN(epDescr); ++ epDescr->hw_len = 0; ++ epDescr->command = IO_FIELD(USB_EP_command, epid, i); ++ epDescr->sub = 0; ++ epDescr->next = virt_to_phys(&TxBulkEPList[i + 1]); ++ ++ /* Initiate two EPs, disabled and with the eol flag set. No need for any ++ preserved epid. */ ++ ++ /* The first one has the intr flag set so we get an interrupt when the DMA ++ channel is about to become disabled. */ ++ CHECK_ALIGN(&TxBulkDummyEPList[i][0]); ++ TxBulkDummyEPList[i][0].hw_len = 0; ++ TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | ++ IO_STATE(USB_EP_command, eol, yes) | ++ IO_STATE(USB_EP_command, intr, yes)); ++ TxBulkDummyEPList[i][0].sub = 0; ++ TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]); ++ ++ /* The second one. */ ++ CHECK_ALIGN(&TxBulkDummyEPList[i][1]); ++ TxBulkDummyEPList[i][1].hw_len = 0; ++ TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | ++ IO_STATE(USB_EP_command, eol, yes)); ++ TxBulkDummyEPList[i][1].sub = 0; ++ /* The last dummy's next pointer is the same as the current EP's next pointer. */ ++ TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]); ++ } ++ ++ /* Special handling of last descr in list, make list circular */ ++ epDescr = &TxBulkEPList[i]; ++ CHECK_ALIGN(epDescr); ++ epDescr->hw_len = 0; ++ epDescr->command = IO_STATE(USB_EP_command, eol, yes) | ++ IO_FIELD(USB_EP_command, epid, i); ++ epDescr->sub = 0; ++ epDescr->next = virt_to_phys(&TxBulkEPList[0]); ++ ++ /* Init DMA sub-channel pointers to last item in each list */ ++ *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]); ++ /* No point in starting the bulk channel yet. ++ *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */ ++} ++ ++static void tc_dma_init_tx_ctrl_list(void) { ++ int i; ++ volatile struct USB_EP_Desc *epDescr; ++ ++ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { ++ epDescr = &(TxCtrlEPList[i]); ++ CHECK_ALIGN(epDescr); ++ epDescr->hw_len = 0; ++ epDescr->command = IO_FIELD(USB_EP_command, epid, i); ++ epDescr->sub = 0; ++ epDescr->next = virt_to_phys(&TxCtrlEPList[i + 1]); ++ } ++ /* Special handling of last descr in list, make list circular */ ++ epDescr = &TxCtrlEPList[i]; ++ CHECK_ALIGN(epDescr); ++ epDescr->hw_len = 0; ++ epDescr->command = IO_STATE(USB_EP_command, eol, yes) | ++ IO_FIELD(USB_EP_command, epid, i); ++ epDescr->sub = 0; ++ epDescr->next = virt_to_phys(&TxCtrlEPList[0]); ++ ++ /* Init DMA sub-channel pointers to last item in each list */ ++ *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[i]); ++ /* No point in starting the ctrl channel yet. ++ *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */ ++} ++ ++ ++static void tc_dma_init_tx_intr_list(void) { ++ int i; ++ ++ TxIntrSB_zout.sw_len = 1; ++ TxIntrSB_zout.next = 0; ++ TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]); ++ TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | ++ IO_STATE(USB_SB_command, tt, zout) | ++ IO_STATE(USB_SB_command, full, yes) | ++ IO_STATE(USB_SB_command, eot, yes) | ++ IO_STATE(USB_SB_command, eol, yes)); ++ ++ for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) { ++ CHECK_ALIGN(&TxIntrEPList[i]); ++ TxIntrEPList[i].hw_len = 0; ++ TxIntrEPList[i].command = ++ (IO_STATE(USB_EP_command, eof, yes) | ++ IO_STATE(USB_EP_command, enable, yes) | ++ IO_FIELD(USB_EP_command, epid, INVALID_EPID)); ++ TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); ++ TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]); ++ } ++ ++ /* Special handling of last descr in list, make list circular */ ++ CHECK_ALIGN(&TxIntrEPList[i]); ++ TxIntrEPList[i].hw_len = 0; ++ TxIntrEPList[i].command = ++ (IO_STATE(USB_EP_command, eof, yes) | ++ IO_STATE(USB_EP_command, eol, yes) | ++ IO_STATE(USB_EP_command, enable, yes) | ++ IO_FIELD(USB_EP_command, epid, INVALID_EPID)); ++ TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); ++ TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]); ++ ++ intr_dbg("Initiated Intr EP descriptor list\n"); ++ ++ ++ /* Connect DMA 8 sub-channel 2 to first in list */ ++ *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]); ++} ++ ++static void tc_dma_init_tx_isoc_list(void) { ++ int i; ++ ++ DBFENTER; ++ ++ /* Read comment at zout_buffer declaration for an explanation to this. */ ++ TxIsocSB_zout.sw_len = 1; ++ TxIsocSB_zout.next = 0; ++ TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]); ++ TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | ++ IO_STATE(USB_SB_command, tt, zout) | ++ IO_STATE(USB_SB_command, full, yes) | ++ IO_STATE(USB_SB_command, eot, yes) | ++ IO_STATE(USB_SB_command, eol, yes)); ++ ++ /* The last isochronous EP descriptor is a dummy. */ ++ for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { ++ CHECK_ALIGN(&TxIsocEPList[i]); ++ TxIsocEPList[i].hw_len = 0; ++ TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i); ++ TxIsocEPList[i].sub = 0; ++ TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]); ++ } ++ ++ CHECK_ALIGN(&TxIsocEPList[i]); ++ TxIsocEPList[i].hw_len = 0; ++ ++ /* Must enable the last EP descr to get eof interrupt. */ ++ TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) | ++ IO_STATE(USB_EP_command, eof, yes) | ++ IO_STATE(USB_EP_command, eol, yes) | ++ IO_FIELD(USB_EP_command, epid, INVALID_EPID)); ++ TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout); ++ TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]); ++ ++ *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]); ++ *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); ++} ++ ++static int tc_dma_init(struct usb_hcd *hcd) { ++ tc_dma_init_rx_list(); ++ tc_dma_init_tx_bulk_list(); ++ tc_dma_init_tx_ctrl_list(); ++ tc_dma_init_tx_intr_list(); ++ tc_dma_init_tx_isoc_list(); ++ ++ if (cris_request_dma(USB_TX_DMA_NBR, ++ "ETRAX 100LX built-in USB (Tx)", ++ DMA_VERBOSE_ON_ERROR, ++ dma_usb)) { ++ err("Could not allocate DMA ch 8 for USB"); ++ return -EBUSY; ++ } ++ ++ if (cris_request_dma(USB_RX_DMA_NBR, ++ "ETRAX 100LX built-in USB (Rx)", ++ DMA_VERBOSE_ON_ERROR, ++ dma_usb)) { ++ err("Could not allocate DMA ch 9 for USB"); ++ return -EBUSY; ++ } ++ ++ *R_IRQ_MASK2_SET = ++ /* Note that these interrupts are not used. */ ++ IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) | ++ /* Sub channel 1 (ctrl) descr. interrupts are used. */ ++ IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) | ++ IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) | ++ /* Sub channel 3 (isoc) descr. interrupts are used. */ ++ IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set); ++ ++ /* Note that the dma9_descr interrupt is not used. */ ++ *R_IRQ_MASK2_SET = ++ IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) | ++ IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); ++ ++ if (request_irq(ETRAX_USB_RX_IRQ, tc_dma_rx_interrupt, 0, ++ "ETRAX 100LX built-in USB (Rx)", hcd)) { ++ err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ); ++ return -EBUSY; ++ } ++ ++ if (request_irq(ETRAX_USB_TX_IRQ, tc_dma_tx_interrupt, 0, ++ "ETRAX 100LX built-in USB (Tx)", hcd)) { ++ err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static void tc_dma_destroy(void) { ++ free_irq(ETRAX_USB_RX_IRQ, NULL); ++ free_irq(ETRAX_USB_TX_IRQ, NULL); ++ ++ cris_free_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)"); ++ cris_free_dma(USB_RX_DMA_NBR, "ETRAX 100LX built-in USB (Rx)"); ++ ++} ++ ++static void tc_dma_link_intr_urb(struct urb *urb); ++ ++/* Handle processing of Bulk, Ctrl and Intr queues */ ++static void tc_dma_process_queue(int epid) { ++ struct urb *urb; ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ unsigned long flags; ++ char toggle; ++ ++ if(epid_state[epid].disabled) { ++ /* Don't process any URBs on a disabled endpoint */ ++ return; ++ } ++ ++ /* Do not disturb us while fiddling with EPs and epids */ ++ local_irq_save(flags); ++ ++ /* For bulk, Ctrl and Intr can we only have one URB active at a time for ++ a specific EP. */ ++ if(activeUrbList[epid] != NULL) { ++ /* An URB is already active on EP, skip checking queue */ ++ local_irq_restore(flags); ++ return; ++ } ++ ++ urb = urb_list_first(epid); ++ if(urb == NULL) { ++ /* No URB waiting in EP queue. Nothing do to */ ++ local_irq_restore(flags); ++ return; ++ } ++ ++ urb_priv = urb->hcpriv; ++ ASSERT(urb_priv != NULL); ++ ASSERT(urb_priv->urb_state == NOT_STARTED); ++ ASSERT(!usb_pipeisoc(urb->pipe)); ++ ++ /* Remove this URB from the queue and move it to active */ ++ activeUrbList[epid] = urb; ++ urb_list_del(urb, epid); ++ ++ urb_priv->urb_state = STARTED; ++ ++ /* Reset error counters (regardless of which direction this traffic is). */ ++ etrax_epid_clear_error(epid); ++ ++ /* Special handling of Intr EP lists */ ++ if(usb_pipeint(urb->pipe)) { ++ tc_dma_link_intr_urb(urb); ++ local_irq_restore(flags); ++ return; ++ } ++ ++ /* Software must preset the toggle bits for Bulk and Ctrl */ ++ if(usb_pipecontrol(urb->pipe)) { ++ /* Toggle bits are initialized only during setup transaction in a ++ CTRL transfer */ ++ etrax_epid_set_toggle(epid, 0, 0); ++ etrax_epid_set_toggle(epid, 1, 0); ++ } else { ++ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), ++ usb_pipeout(urb->pipe)); ++ etrax_epid_set_toggle(epid, usb_pipeout(urb->pipe), toggle); ++ } ++ ++ tc_dbg("Added SBs from (URB:0x%x %s %s) to epid %d: %s\n", ++ (unsigned int)urb, str_dir(urb->pipe), str_type(urb->pipe), epid, ++ sblist_to_str(urb_priv->first_sb)); ++ ++ /* We start the DMA sub channel without checking if it's running or not, ++ because: ++ 1) If it's already running, issuing the start command is a nop. ++ 2) We avoid a test-and-set race condition. */ ++ switch(usb_pipetype(urb->pipe)) { ++ case PIPE_BULK: ++ /* Assert that the EP descriptor is disabled. */ ++ ASSERT(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); ++ ++ /* Set up and enable the EP descriptor. */ ++ TxBulkEPList[epid].sub = virt_to_phys(urb_priv->first_sb); ++ TxBulkEPList[epid].hw_len = 0; ++ TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); ++ ++ /* Check if the dummy list is already with us (if several urbs were queued). */ ++ if (usb_pipein(urb->pipe) && (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0]))) { ++ tc_dbg("Inviting dummy list to the party for urb 0x%lx, epid %d", ++ (unsigned long)urb, epid); ++ ++ /* We don't need to check if the DMA is at this EP or not before changing the ++ next pointer, since we will do it in one 32-bit write (EP descriptors are ++ 32-bit aligned). */ ++ TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]); ++ } ++ ++ restart_dma8_sub0(); ++ ++ /* Update/restart the bulk start timer since we just started the channel.*/ ++ mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); ++ /* Update/restart the bulk eot timer since we just inserted traffic. */ ++ mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); ++ break; ++ case PIPE_CONTROL: ++ /* Assert that the EP descriptor is disabled. */ ++ ASSERT(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); ++ ++ /* Set up and enable the EP descriptor. */ ++ TxCtrlEPList[epid].sub = virt_to_phys(urb_priv->first_sb); ++ TxCtrlEPList[epid].hw_len = 0; ++ TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); ++ ++ *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); ++ break; ++ } ++ local_irq_restore(flags); ++} ++ ++static void tc_dma_link_intr_urb(struct urb *urb) { ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ volatile struct USB_EP_Desc *tmp_ep; ++ struct USB_EP_Desc *ep_desc; ++ int i = 0, epid; ++ int pool_idx = 0; ++ ++ ASSERT(urb_priv != NULL); ++ epid = urb_priv->epid; ++ ASSERT(urb_priv->interval > 0); ++ ASSERT(urb_priv->intr_ep_pool_length > 0); ++ ++ tmp_ep = &TxIntrEPList[0]; ++ ++ /* Only insert one EP descriptor in list for Out Intr URBs. ++ We can only handle Out Intr with interval of 128ms because ++ it's not possible to insert several Out Intr EPs because they ++ are not consumed by the DMA. */ ++ if(usb_pipeout(urb->pipe)) { ++ ep_desc = urb_priv->intr_ep_pool[0]; ++ ASSERT(ep_desc); ++ ep_desc->next = tmp_ep->next; ++ tmp_ep->next = virt_to_phys(ep_desc); ++ i++; ++ } else { ++ /* Loop through Intr EP descriptor list and insert EP for URB at ++ specified interval */ ++ do { ++ /* Each EP descriptor with eof flag sat signals a new frame */ ++ if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) { ++ /* Insert a EP from URBs EP pool at correct interval */ ++ if ((i % urb_priv->interval) == 0) { ++ ep_desc = urb_priv->intr_ep_pool[pool_idx]; ++ ASSERT(ep_desc); ++ ep_desc->next = tmp_ep->next; ++ tmp_ep->next = virt_to_phys(ep_desc); ++ pool_idx++; ++ ASSERT(pool_idx <= urb_priv->intr_ep_pool_length); ++ } ++ i++; ++ } ++ tmp_ep = (struct USB_EP_Desc *)phys_to_virt(tmp_ep->next); ++ } while(tmp_ep != &TxIntrEPList[0]); ++ } ++ ++ intr_dbg("Added SBs to intr epid %d: %s interval:%d (%d EP)\n", epid, ++ sblist_to_str(urb_priv->first_sb), urb_priv->interval, pool_idx); ++ ++ /* We start the DMA sub channel without checking if it's running or not, ++ because: ++ 1) If it's already running, issuing the start command is a nop. ++ 2) We avoid a test-and-set race condition. */ ++ *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); ++} ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++static void tc_dma_process_isoc_urb(struct urb *urb) { ++ unsigned long flags; ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ int epid; ++ ++ /* Do not disturb us while fiddling with EPs and epids */ ++ local_irq_save(flags); ++ ++ ASSERT(urb_priv); ++ ASSERT(urb_priv->first_sb); ++ epid = urb_priv->epid; ++ ++ if(activeUrbList[epid] == NULL) { ++ /* EP is idle, so make this URB active */ ++ activeUrbList[epid] = urb; ++ urb_list_del(urb, epid); ++ ASSERT(TxIsocEPList[epid].sub == 0); ++ ASSERT(!(TxIsocEPList[epid].command & ++ IO_STATE(USB_EP_command, enable, yes))); ++ ++ /* Differentiate between In and Out Isoc. Because In SBs are not consumed*/ ++ if(usb_pipein(urb->pipe)) { ++ /* Each EP for In Isoc will have only one SB descriptor, setup when ++ submitting the first active urb. We do it here by copying from URBs ++ pre-allocated SB. */ ++ memcpy((void *)&(TxIsocSBList[epid]), urb_priv->first_sb, ++ sizeof(TxIsocSBList[epid])); ++ TxIsocEPList[epid].hw_len = 0; ++ TxIsocEPList[epid].sub = virt_to_phys(&(TxIsocSBList[epid])); ++ } else { ++ /* For Out Isoc we attach the pre-allocated list of SBs for the URB */ ++ TxIsocEPList[epid].hw_len = 0; ++ TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); ++ ++ isoc_dbg("Attached first URB:0x%x[%d] to epid:%d first_sb:0x%x" ++ " last_sb::0x%x\n", ++ (unsigned int)urb, urb_priv->urb_num, epid, ++ (unsigned int)(urb_priv->first_sb), ++ (unsigned int)(urb_priv->last_sb)); ++ } ++ ++ if (urb->transfer_flags & URB_ISO_ASAP) { ++ /* The isoc transfer should be started as soon as possible. The ++ start_frame field is a return value if URB_ISO_ASAP was set. Comparing ++ R_USB_FM_NUMBER with a USB Chief trace shows that the first isoc IN ++ token is sent 2 frames later. I'm not sure how this affects usage of ++ the start_frame field by the device driver, or how it affects things ++ when USB_ISO_ASAP is not set, so therefore there's no compensation for ++ the 2 frame "lag" here. */ ++ urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff); ++ TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); ++ urb_priv->urb_state = STARTED; ++ isoc_dbg("URB_ISO_ASAP set, urb->start_frame set to %d\n", ++ urb->start_frame); ++ } else { ++ /* Not started yet. */ ++ urb_priv->urb_state = NOT_STARTED; ++ isoc_warn("urb_priv->urb_state set to NOT_STARTED for URB:0x%x\n", ++ (unsigned int)urb); ++ } ++ ++ } else { ++ /* An URB is already active on the EP. Leave URB in queue and let ++ finish_isoc_urb process it after current active URB */ ++ ASSERT(TxIsocEPList[epid].sub != 0); ++ ++ if(usb_pipein(urb->pipe)) { ++ /* Because there already is a active In URB on this epid we do nothing ++ and the finish_isoc_urb() function will handle switching to next URB*/ ++ ++ } else { /* For Out Isoc, insert new URBs traffic last in SB-list. */ ++ struct USB_SB_Desc *temp_sb_desc; ++ ++ /* Set state STARTED to all Out Isoc URBs added to SB list because we ++ don't know how many of them that are finished before descr interrupt*/ ++ urb_priv->urb_state = STARTED; ++ ++ /* Find end of current SB list by looking for SB with eol flag sat */ ++ temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub); ++ while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) != ++ IO_STATE(USB_SB_command, eol, yes)) { ++ ASSERT(temp_sb_desc->next); ++ temp_sb_desc = phys_to_virt(temp_sb_desc->next); ++ } ++ ++ isoc_dbg("Appended URB:0x%x[%d] (first:0x%x last:0x%x) to epid:%d" ++ " sub:0x%x eol:0x%x\n", ++ (unsigned int)urb, urb_priv->urb_num, ++ (unsigned int)(urb_priv->first_sb), ++ (unsigned int)(urb_priv->last_sb), epid, ++ (unsigned int)phys_to_virt(TxIsocEPList[epid].sub), ++ (unsigned int)temp_sb_desc); ++ ++ /* Next pointer must be set before eol is removed. */ ++ temp_sb_desc->next = virt_to_phys(urb_priv->first_sb); ++ /* Clear the previous end of list flag since there is a new in the ++ added SB descriptor list. */ ++ temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol); ++ ++ if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { ++ __u32 epid_data; ++ /* 8.8.5 in Designer's Reference says we should check for and correct ++ any errors in the EP here. That should not be necessary if ++ epid_attn is handled correctly, so we assume all is ok. */ ++ epid_data = etrax_epid_iso_get(epid); ++ if (IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data) != ++ IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { ++ isoc_err("Disabled Isoc EP with error:%d on epid:%d when appending" ++ " URB:0x%x[%d]\n", ++ IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data), epid, ++ (unsigned int)urb, urb_priv->urb_num); ++ } ++ ++ /* The SB list was exhausted. */ ++ if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) { ++ /* The new sublist did not get processed before the EP was ++ disabled. Setup the EP again. */ ++ ++ if(virt_to_phys(temp_sb_desc) == TxIsocEPList[epid].sub) { ++ isoc_dbg("EP for epid:%d stoped at SB:0x%x before newly inserted" ++ ", restarting from this URBs SB:0x%x\n", ++ epid, (unsigned int)temp_sb_desc, ++ (unsigned int)(urb_priv->first_sb)); ++ TxIsocEPList[epid].hw_len = 0; ++ TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); ++ urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff); ++ /* Enable the EP again so data gets processed this time */ ++ TxIsocEPList[epid].command |= ++ IO_STATE(USB_EP_command, enable, yes); ++ ++ } else { ++ /* The EP has been disabled but not at end this URB (god knows ++ where). This should generate an epid_attn so we should not be ++ here */ ++ isoc_warn("EP was disabled on sb:0x%x before SB list for" ++ " URB:0x%x[%d] got processed\n", ++ (unsigned int)phys_to_virt(TxIsocEPList[epid].sub), ++ (unsigned int)urb, urb_priv->urb_num); ++ } ++ } else { ++ /* This might happend if we are slow on this function and isn't ++ an error. */ ++ isoc_dbg("EP was disabled and finished with SBs from appended" ++ " URB:0x%x[%d]\n", (unsigned int)urb, urb_priv->urb_num); ++ } ++ } ++ } ++ } ++ ++ /* Start the DMA sub channel */ ++ *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); ++ ++ local_irq_restore(flags); ++} ++#endif ++ ++static void tc_dma_unlink_intr_urb(struct urb *urb) { ++ struct crisv10_urb_priv *urb_priv = urb->hcpriv; ++ volatile struct USB_EP_Desc *first_ep; /* First EP in the list. */ ++ volatile struct USB_EP_Desc *curr_ep; /* Current EP, the iterator. */ ++ volatile struct USB_EP_Desc *next_ep; /* The EP after current. */ ++ volatile struct USB_EP_Desc *unlink_ep; /* The one we should remove from ++ the list. */ ++ int count = 0; ++ volatile int timeout = 10000; ++ int epid; ++ ++ /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the ++ List". */ ++ ASSERT(urb_priv); ++ ASSERT(urb_priv->intr_ep_pool_length > 0); ++ epid = urb_priv->epid; ++ ++ /* First disable all Intr EPs belonging to epid for this URB */ ++ first_ep = &TxIntrEPList[0]; ++ curr_ep = first_ep; ++ do { ++ next_ep = (struct USB_EP_Desc *)phys_to_virt(curr_ep->next); ++ if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { ++ /* Disable EP */ ++ next_ep->command &= ~IO_MASK(USB_EP_command, enable); ++ } ++ curr_ep = phys_to_virt(curr_ep->next); ++ } while (curr_ep != first_ep); ++ ++ ++ /* Now unlink all EPs belonging to this epid from Descr list */ ++ first_ep = &TxIntrEPList[0]; ++ curr_ep = first_ep; ++ do { ++ next_ep = (struct USB_EP_Desc *)phys_to_virt(curr_ep->next); ++ if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { ++ /* This is the one we should unlink. */ ++ unlink_ep = next_ep; ++ ++ /* Actually unlink the EP from the DMA list. */ ++ curr_ep->next = unlink_ep->next; ++ ++ /* Wait until the DMA is no longer at this descriptor. */ ++ while((*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep)) && ++ (timeout-- > 0)); ++ if(timeout == 0) { ++ warn("Timeout while waiting for DMA-TX-Intr to leave unlink EP\n"); ++ } ++ ++ count++; ++ } ++ curr_ep = phys_to_virt(curr_ep->next); ++ } while (curr_ep != first_ep); ++ ++ if(count != urb_priv->intr_ep_pool_length) { ++ intr_warn("Unlinked %d of %d Intr EPs for URB:0x%x[%d]\n", count, ++ urb_priv->intr_ep_pool_length, (unsigned int)urb, ++ urb_priv->urb_num); ++ } else { ++ intr_dbg("Unlinked %d of %d interrupt EPs for URB:0x%x\n", count, ++ urb_priv->intr_ep_pool_length, (unsigned int)urb); ++ } ++} ++ ++static void check_finished_bulk_tx_epids(struct usb_hcd *hcd, ++ int timer) { ++ unsigned long flags; ++ int epid; ++ struct urb *urb; ++ struct crisv10_urb_priv * urb_priv; ++ __u32 epid_data; ++ ++ /* Protect TxEPList */ ++ local_irq_save(flags); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS; epid++) { ++ /* A finished EP descriptor is disabled and has a valid sub pointer */ ++ if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) && ++ (TxBulkEPList[epid].sub != 0)) { ++ ++ /* Get the active URB for this epid */ ++ urb = activeUrbList[epid]; ++ /* Sanity checks */ ++ ASSERT(urb); ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ ++ /* Only handle finished out Bulk EPs here, ++ and let RX interrupt take care of the rest */ ++ if(!epid_out_traffic(epid)) { ++ continue; ++ } ++ ++ if(timer) { ++ tc_warn("Found finished %s Bulk epid:%d URB:0x%x[%d] from timeout\n", ++ epid_out_traffic(epid) ? "Out" : "In", epid, (unsigned int)urb, ++ urb_priv->urb_num); ++ } else { ++ tc_dbg("Found finished %s Bulk epid:%d URB:0x%x[%d] from interrupt\n", ++ epid_out_traffic(epid) ? "Out" : "In", epid, (unsigned int)urb, ++ urb_priv->urb_num); ++ } ++ ++ if(urb_priv->urb_state == UNLINK) { ++ /* This Bulk URB is requested to be unlinked, that means that the EP ++ has been disabled and we might not have sent all data */ ++ tc_finish_urb(hcd, urb, urb->status); ++ continue; ++ } ++ ++ ASSERT(urb_priv->urb_state == STARTED); ++ if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) { ++ tc_err("Endpoint got disabled before reaching last sb\n"); ++ } ++ ++ epid_data = etrax_epid_get(epid); ++ if (IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data) == ++ IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { ++ /* This means that the endpoint has no error, is disabled ++ and had inserted traffic, i.e. transfer successfully completed. */ ++ tc_finish_urb(hcd, urb, 0); ++ } else { ++ /* Shouldn't happen. We expect errors to be caught by epid ++ attention. */ ++ tc_err("Found disabled bulk EP desc (epid:%d error:%d)\n", ++ epid, IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data)); ++ } ++ } else { ++ tc_dbg("Ignoring In Bulk epid:%d, let RX interrupt handle it\n", epid); ++ } ++ } ++ ++ local_irq_restore(flags); ++} ++ ++static void check_finished_ctrl_tx_epids(struct usb_hcd *hcd) { ++ unsigned long flags; ++ int epid; ++ struct urb *urb; ++ struct crisv10_urb_priv * urb_priv; ++ __u32 epid_data; ++ ++ /* Protect TxEPList */ ++ local_irq_save(flags); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS; epid++) { ++ if(epid == DUMMY_EPID) ++ continue; ++ ++ /* A finished EP descriptor is disabled and has a valid sub pointer */ ++ if (!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) && ++ (TxCtrlEPList[epid].sub != 0)) { ++ ++ /* Get the active URB for this epid */ ++ urb = activeUrbList[epid]; ++ ++ if(urb == NULL) { ++ tc_warn("Found finished Ctrl epid:%d with no active URB\n", epid); ++ continue; ++ } ++ ++ /* Sanity checks */ ++ ASSERT(usb_pipein(urb->pipe)); ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ if (phys_to_virt(TxCtrlEPList[epid].sub) != urb_priv->last_sb) { ++ tc_err("Endpoint got disabled before reaching last sb\n"); ++ } ++ ++ epid_data = etrax_epid_get(epid); ++ if (IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data) == ++ IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { ++ /* This means that the endpoint has no error, is disabled ++ and had inserted traffic, i.e. transfer successfully completed. */ ++ ++ /* Check if RX-interrupt for In Ctrl has been processed before ++ finishing the URB */ ++ if(urb_priv->ctrl_rx_done) { ++ tc_dbg("Finishing In Ctrl URB:0x%x[%d] in tx_interrupt\n", ++ (unsigned int)urb, urb_priv->urb_num); ++ tc_finish_urb(hcd, urb, 0); ++ } else { ++ /* If we get zout descriptor interrupt before RX was done for a ++ In Ctrl transfer, then we flag that and it will be finished ++ in the RX-Interrupt */ ++ urb_priv->ctrl_zout_done = 1; ++ tc_dbg("Got zout descr interrupt before RX interrupt\n"); ++ } ++ } else { ++ /* Shouldn't happen. We expect errors to be caught by epid ++ attention. */ ++ tc_err("Found disabled Ctrl EP desc (epid:%d URB:0x%x[%d]) error_code:%d\n", epid, (unsigned int)urb, urb_priv->urb_num, IO_EXTRACT(R_USB_EPT_DATA, error_code, epid_data)); ++ __dump_ep_desc(&(TxCtrlEPList[epid])); ++ __dump_ept_data(epid); ++ } ++ } ++ } ++ local_irq_restore(flags); ++} ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++/* This function goes through all epids that are setup for Out Isoc transfers ++ and marks (isoc_out_done) all queued URBs that the DMA has finished ++ transfer for. ++ No URB completetion is done here to make interrupt routine return quickly. ++ URBs are completed later with help of complete_isoc_bottom_half() that ++ becomes schedules when this functions is finished. */ ++static void check_finished_isoc_tx_epids(void) { ++ unsigned long flags; ++ int epid; ++ struct urb *urb; ++ struct crisv10_urb_priv * urb_priv; ++ struct USB_SB_Desc* sb_desc; ++ int epid_done; ++ ++ /* Protect TxIsocEPList */ ++ local_irq_save(flags); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS; epid++) { ++ if (TxIsocEPList[epid].sub == 0 || epid == INVALID_EPID || ++ !epid_out_traffic(epid)) { ++ /* Nothing here to see. */ ++ continue; ++ } ++ ASSERT(epid_inuse(epid)); ++ ASSERT(epid_isoc(epid)); ++ ++ sb_desc = phys_to_virt(TxIsocEPList[epid].sub); ++ /* Find the last descriptor of the currently active URB for this ep. ++ This is the first descriptor in the sub list marked for a descriptor ++ interrupt. */ ++ while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) { ++ sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0; ++ } ++ ASSERT(sb_desc); ++ ++ isoc_dbg("Descr IRQ checking epid:%d sub:0x%x intr:0x%x\n", ++ epid, (unsigned int)phys_to_virt(TxIsocEPList[epid].sub), ++ (unsigned int)sb_desc); ++ ++ urb = activeUrbList[epid]; ++ if(urb == NULL) { ++ isoc_err("Isoc Descr irq on epid:%d with no active URB\n", epid); ++ continue; ++ } ++ ++ epid_done = 0; ++ while(urb && !epid_done) { ++ /* Sanity check. */ ++ ASSERT(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); ++ ASSERT(usb_pipeout(urb->pipe)); ++ ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ ASSERT(urb_priv->urb_state == STARTED || ++ urb_priv->urb_state == UNLINK); ++ ++ if (sb_desc != urb_priv->last_sb) { ++ /* This urb has been sent. */ ++ urb_priv->isoc_out_done = 1; ++ ++ } else { /* Found URB that has last_sb as the interrupt reason */ ++ ++ /* Check if EP has been disabled, meaning that all transfers are done*/ ++ if(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { ++ ASSERT((sb_desc->command & IO_MASK(USB_SB_command, eol)) == ++ IO_STATE(USB_SB_command, eol, yes)); ++ ASSERT(sb_desc->next == 0); ++ urb_priv->isoc_out_done = 1; ++ } else { ++ isoc_dbg("Skipping URB:0x%x[%d] because EP not disabled yet\n", ++ (unsigned int)urb, urb_priv->urb_num); ++ } ++ /* Stop looking any further in queue */ ++ epid_done = 1; ++ } ++ ++ if (!epid_done) { ++ if(urb == activeUrbList[epid]) { ++ urb = urb_list_first(epid); ++ } else { ++ urb = urb_list_next(urb, epid); ++ } ++ } ++ } /* END: while(urb && !epid_done) */ ++ } ++ ++ local_irq_restore(flags); ++} ++ ++ ++/* This is where the Out Isoc URBs are realy completed. This function is ++ scheduled from tc_dma_tx_interrupt() when one or more Out Isoc transfers ++ are done. This functions completes all URBs earlier marked with ++ isoc_out_done by fast interrupt routine check_finished_isoc_tx_epids() */ ++ ++static void complete_isoc_bottom_half(void *data) { ++ struct crisv10_isoc_complete_data *comp_data; ++ struct usb_iso_packet_descriptor *packet; ++ struct crisv10_urb_priv * urb_priv; ++ unsigned long flags; ++ struct urb* urb; ++ int epid_done; ++ int epid; ++ int i; ++ ++ comp_data = (struct crisv10_isoc_complete_data*)data; ++ ++ local_irq_save(flags); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { ++ if(!epid_inuse(epid) || !epid_isoc(epid) || !epid_out_traffic(epid) || epid == DUMMY_EPID) { ++ /* Only check valid Out Isoc epids */ ++ continue; ++ } ++ ++ isoc_dbg("Isoc bottom-half checking epid:%d, sub:0x%x\n", epid, ++ (unsigned int)phys_to_virt(TxIsocEPList[epid].sub)); ++ ++ /* The descriptor interrupt handler has marked all transmitted Out Isoc ++ URBs with isoc_out_done. Now we traverse all epids and for all that ++ have out Isoc traffic we traverse its URB list and complete the ++ transmitted URBs. */ ++ epid_done = 0; ++ while (!epid_done) { ++ ++ /* Get the active urb (if any) */ ++ urb = activeUrbList[epid]; ++ if (urb == 0) { ++ isoc_dbg("No active URB on epid:%d anymore\n", epid); ++ epid_done = 1; ++ continue; ++ } ++ ++ /* Sanity check. */ ++ ASSERT(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); ++ ASSERT(usb_pipeout(urb->pipe)); ++ ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ ++ if (!(urb_priv->isoc_out_done)) { ++ /* We have reached URB that isn't flaged done yet, stop traversing. */ ++ isoc_dbg("Stoped traversing Out Isoc URBs on epid:%d" ++ " before not yet flaged URB:0x%x[%d]\n", ++ epid, (unsigned int)urb, urb_priv->urb_num); ++ epid_done = 1; ++ continue; ++ } ++ ++ /* This urb has been sent. */ ++ isoc_dbg("Found URB:0x%x[%d] that is flaged isoc_out_done\n", ++ (unsigned int)urb, urb_priv->urb_num); ++ ++ /* Set ok on transfered packets for this URB and finish it */ ++ for (i = 0; i < urb->number_of_packets; i++) { ++ packet = &urb->iso_frame_desc[i]; ++ packet->status = 0; ++ packet->actual_length = packet->length; ++ } ++ urb_priv->isoc_packet_counter = urb->number_of_packets; ++ tc_finish_urb(comp_data->hcd, urb, 0); ++ ++ } /* END: while(!epid_done) */ ++ } /* END: for(epid...) */ ++ ++ local_irq_restore(flags); ++ kmem_cache_free(isoc_compl_cache, comp_data); ++} ++#endif ++ ++static void check_finished_intr_tx_epids(struct usb_hcd *hcd) { ++ unsigned long flags; ++ int epid; ++ struct urb *urb; ++ struct crisv10_urb_priv * urb_priv; ++ volatile struct USB_EP_Desc *curr_ep; /* Current EP, the iterator. */ ++ volatile struct USB_EP_Desc *next_ep; /* The EP after current. */ ++ ++ /* Protect TxintrEPList */ ++ local_irq_save(flags); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS; epid++) { ++ if(!epid_inuse(epid) || !epid_intr(epid) || !epid_out_traffic(epid)) { ++ /* Nothing to see on this epid. Only check valid Out Intr epids */ ++ continue; ++ } ++ ++ urb = activeUrbList[epid]; ++ if(urb == 0) { ++ intr_warn("Found Out Intr epid:%d with no active URB\n", epid); ++ continue; ++ } ++ ++ /* Sanity check. */ ++ ASSERT(usb_pipetype(urb->pipe) == PIPE_INTERRUPT); ++ ASSERT(usb_pipeout(urb->pipe)); ++ ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ ++ /* Go through EPs between first and second sof-EP. It's here Out Intr EPs ++ are inserted.*/ ++ curr_ep = &TxIntrEPList[0]; ++ do { ++ next_ep = (struct USB_EP_Desc *)phys_to_virt(curr_ep->next); ++ if(next_ep == urb_priv->intr_ep_pool[0]) { ++ /* We found the Out Intr EP for this epid */ ++ ++ /* Disable it so it doesn't get processed again */ ++ next_ep->command &= ~IO_MASK(USB_EP_command, enable); ++ ++ /* Finish the active Out Intr URB with status OK */ ++ tc_finish_urb(hcd, urb, 0); ++ } ++ curr_ep = phys_to_virt(curr_ep->next); ++ } while (curr_ep != &TxIntrEPList[1]); ++ ++ } ++ local_irq_restore(flags); ++} ++ ++/* Interrupt handler for DMA8/IRQ24 with subchannels (called from hardware intr) */ ++static irqreturn_t tc_dma_tx_interrupt(int irq, void *vhc) { ++ struct usb_hcd *hcd = (struct usb_hcd*)vhc; ++ ASSERT(hcd); ++ ++ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { ++ /* Clear this interrupt */ ++ *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); ++ restart_dma8_sub0(); ++ } ++ ++ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { ++ /* Clear this interrupt */ ++ *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); ++ check_finished_ctrl_tx_epids(hcd); ++ } ++ ++ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { ++ /* Clear this interrupt */ ++ *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); ++ check_finished_intr_tx_epids(hcd); ++ } ++ ++ /* hinko ignore usb_pipeisoc */ ++#if 0 ++ if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { ++ struct crisv10_isoc_complete_data* comp_data; ++ ++ /* Flag done Out Isoc for later completion */ ++ check_finished_isoc_tx_epids(); ++ ++ /* Clear this interrupt */ ++ *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); ++ /* Schedule bottom half of Out Isoc completion function. This function ++ finishes the URBs marked with isoc_out_done */ ++ comp_data = (struct crisv10_isoc_complete_data*) ++ kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC); ++ ASSERT(comp_data != NULL); ++ comp_data ->hcd = hcd; ++ ++ //INIT_WORK(&comp_data->usb_bh, complete_isoc_bottom_half, comp_data); ++ INIT_WORK(&comp_data->usb_bh, complete_isoc_bottom_half); ++ schedule_work(&comp_data->usb_bh); ++ } ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++/* Interrupt handler for DMA9/IRQ25 (called from hardware intr) */ ++static irqreturn_t tc_dma_rx_interrupt(int irq, void *vhc) { ++ unsigned long flags; ++ struct urb *urb; ++ struct usb_hcd *hcd = (struct usb_hcd*)vhc; ++ struct crisv10_urb_priv *urb_priv; ++ int epid = 0; ++ int real_error; ++ ++ ASSERT(hcd); ++ ++ /* Clear this interrupt. */ ++ *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); ++ ++ /* Custom clear interrupt for this interrupt */ ++ /* The reason we cli here is that we call the driver's callback functions. */ ++ local_irq_save(flags); ++ ++ /* Note that this while loop assumes that all packets span only ++ one rx descriptor. */ ++ while(myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { ++ epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); ++ /* Get the active URB for this epid */ ++ urb = activeUrbList[epid]; ++ ++ ASSERT(epid_inuse(epid)); ++ if (!urb) { ++ dma_err("No urb for epid %d in rx interrupt\n", epid); ++ goto skip_out; ++ } ++ ++ /* Check if any errors on epid */ ++ real_error = 0; ++ if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { ++ __u32 r_usb_ept_data; ++ ++ if (usb_pipeisoc(urb->pipe)) { ++ r_usb_ept_data = etrax_epid_iso_get(epid); ++ if((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) && ++ (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) && ++ (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) { ++ /* Not an error, just a failure to receive an expected iso ++ in packet in this frame. This is not documented ++ in the designers reference. Continue processing. ++ */ ++ } else real_error = 1; ++ } else real_error = 1; ++ } ++ ++ if(real_error) { ++ dma_err("Error in RX descr on epid:%d for URB 0x%x", ++ epid, (unsigned int)urb); ++ dump_ept_data(epid); ++ dump_in_desc(myNextRxDesc); ++ goto skip_out; ++ } ++ ++ urb_priv = (struct crisv10_urb_priv *)urb->hcpriv; ++ ASSERT(urb_priv); ++ ASSERT(urb_priv->urb_state == STARTED || ++ urb_priv->urb_state == UNLINK); ++ ++ if ((usb_pipetype(urb->pipe) == PIPE_BULK) || ++ (usb_pipetype(urb->pipe) == PIPE_CONTROL) || ++ (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { ++ ++ /* We get nodata for empty data transactions, and the rx descriptor's ++ hw_len field is not valid in that case. No data to copy in other ++ words. */ ++ if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { ++ /* No data to copy */ ++ } else { ++ /* ++ dma_dbg("Processing RX for URB:0x%x epid:%d (data:%d ofs:%d)\n", ++ (unsigned int)urb, epid, myNextRxDesc->hw_len, ++ urb_priv->rx_offset); ++ */ ++ /* Only copy data if URB isn't flaged to be unlinked*/ ++ if(urb_priv->urb_state != UNLINK) { ++ /* Make sure the data fits in the buffer. */ ++ if(urb_priv->rx_offset + myNextRxDesc->hw_len ++ <= urb->transfer_buffer_length) { ++ ++ /* Copy the data to URBs buffer */ ++ memcpy(urb->transfer_buffer + urb_priv->rx_offset, ++ phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len); ++ urb_priv->rx_offset += myNextRxDesc->hw_len; ++ } else { ++ /* Signal overflow when returning URB */ ++ urb->status = -EOVERFLOW; ++ tc_finish_urb_later(hcd, urb, urb->status); ++ } ++ } ++ } ++ ++ /* Check if it was the last packet in the transfer */ ++ if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) { ++ /* Special handling for In Ctrl URBs. */ ++ if(usb_pipecontrol(urb->pipe) && usb_pipein(urb->pipe) && ++ !(urb_priv->ctrl_zout_done)) { ++ /* Flag that RX part of Ctrl transfer is done. Because zout descr ++ interrupt hasn't happend yet will the URB be finished in the ++ TX-Interrupt. */ ++ urb_priv->ctrl_rx_done = 1; ++ tc_dbg("Not finishing In Ctrl URB:0x%x from rx_interrupt, waiting" ++ " for zout\n", (unsigned int)urb); ++ } else { ++ tc_finish_urb(hcd, urb, 0); ++ } ++ } ++ } else { /* ISOC RX */ ++ /* ++ isoc_dbg("Processing RX for epid:%d (URB:0x%x) ISOC pipe\n", ++ epid, (unsigned int)urb); ++ */ ++ ++ struct usb_iso_packet_descriptor *packet; ++ ++ if (urb_priv->urb_state == UNLINK) { ++ isoc_warn("Ignoring Isoc Rx data for urb being unlinked.\n"); ++ goto skip_out; ++ } else if (urb_priv->urb_state == NOT_STARTED) { ++ isoc_err("What? Got Rx data for Isoc urb that isn't started?\n"); ++ goto skip_out; ++ } ++ ++ packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter]; ++ ASSERT(packet); ++ packet->status = 0; ++ ++ if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { ++ /* We get nodata for empty data transactions, and the rx descriptor's ++ hw_len field is not valid in that case. We copy 0 bytes however to ++ stay in synch. */ ++ packet->actual_length = 0; ++ } else { ++ packet->actual_length = myNextRxDesc->hw_len; ++ /* Make sure the data fits in the buffer. */ ++ ASSERT(packet->actual_length <= packet->length); ++ memcpy(urb->transfer_buffer + packet->offset, ++ phys_to_virt(myNextRxDesc->buf), packet->actual_length); ++ if(packet->actual_length > 0) ++ isoc_dbg("Copied %d bytes, packet %d for URB:0x%x[%d]\n", ++ packet->actual_length, urb_priv->isoc_packet_counter, ++ (unsigned int)urb, urb_priv->urb_num); ++ } ++ ++ /* Increment the packet counter. */ ++ urb_priv->isoc_packet_counter++; ++ ++ /* Note that we don't care about the eot field in the rx descriptor's ++ status. It will always be set for isoc traffic. */ ++ if (urb->number_of_packets == urb_priv->isoc_packet_counter) { ++ /* Complete the urb with status OK. */ ++ tc_finish_urb(hcd, urb, 0); ++ } ++ } ++ ++ skip_out: ++ myNextRxDesc->status = 0; ++ myNextRxDesc->command |= IO_MASK(USB_IN_command, eol); ++ myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol); ++ myLastRxDesc = myNextRxDesc; ++ myNextRxDesc = phys_to_virt(myNextRxDesc->next); ++ flush_etrax_cache(); ++ *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, restart); ++ } ++ ++ local_irq_restore(flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static void tc_bulk_start_timer_func(unsigned long dummy) { ++ /* We might enable an EP descriptor behind the current DMA position when ++ it's about to decide that there are no more bulk traffic and it should ++ stop the bulk channel. ++ Therefore we periodically check if the bulk channel is stopped and there ++ is an enabled bulk EP descriptor, in which case we start the bulk ++ channel. */ ++ ++ if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { ++ int epid; ++ ++ timer_dbg("bulk_start_timer: Bulk DMA channel not running.\n"); ++ ++ for (epid = 0; epid < NBR_OF_EPIDS; epid++) { ++ if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { ++ timer_warn("Found enabled EP for epid %d, starting bulk channel.\n", ++ epid); ++ restart_dma8_sub0(); ++ ++ /* Restart the bulk eot timer since we just started the bulk channel.*/ ++ mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); ++ ++ /* No need to search any further. */ ++ break; ++ } ++ } ++ } else { ++ timer_dbg("bulk_start_timer: Bulk DMA channel running.\n"); ++ } ++} ++ ++static void tc_bulk_eot_timer_func(unsigned long dummy) { ++ struct usb_hcd *hcd = (struct usb_hcd*)dummy; ++ ASSERT(hcd); ++ /* Because of a race condition in the top half, we might miss a bulk eot. ++ This timer "simulates" a bulk eot if we don't get one for a while, ++ hopefully correcting the situation. */ ++ timer_dbg("bulk_eot_timer timed out.\n"); ++ check_finished_bulk_tx_epids(hcd, 1); ++} ++ ++ ++/*************************************************************/ ++/*************************************************************/ ++/* Device driver block */ ++/*************************************************************/ ++/*************************************************************/ ++ ++/* Forward declarations for device driver functions */ ++static int devdrv_hcd_probe(struct device *); ++static int devdrv_hcd_remove(struct device *); ++#ifdef CONFIG_PM ++static int devdrv_hcd_suspend(struct device *, u32, u32); ++static int devdrv_hcd_resume(struct device *, u32); ++#endif /* CONFIG_PM */ ++ ++/* the device */ ++static struct platform_device *devdrv_hc_platform_device; ++ ++/* device driver interface */ ++static struct device_driver devdrv_hc_device_driver = { ++ .name = (char *) hc_name, ++ .bus = &platform_bus_type, ++ ++ .probe = devdrv_hcd_probe, ++ .remove = devdrv_hcd_remove, ++ ++#ifdef CONFIG_PM ++ .suspend = devdrv_hcd_suspend, ++ .resume = devdrv_hcd_resume, ++#endif /* CONFIG_PM */ ++}; ++ ++/* initialize the host controller and driver */ ++static int __init_or_module devdrv_hcd_probe(struct device *dev) ++{ ++ struct usb_hcd *hcd; ++ struct crisv10_hcd *crisv10_hcd; ++ int retval; ++ ++ /* Check DMA burst length */ ++ if(IO_EXTRACT(R_BUS_CONFIG, dma_burst, *R_BUS_CONFIG) != ++ IO_STATE(R_BUS_CONFIG, dma_burst, burst32)) { ++ devdrv_err("Invalid DMA burst length in Etrax 100LX," ++ " needs to be 32\n"); ++ return -EPERM; ++ } ++ ++ hcd = usb_create_hcd(&crisv10_hc_driver, dev, dev->bus_id); ++ if (!hcd) ++ return -ENOMEM; ++ ++ crisv10_hcd = hcd_to_crisv10_hcd(hcd); ++ spin_lock_init(&crisv10_hcd->lock); ++ crisv10_hcd->num_ports = num_ports(); ++ crisv10_hcd->running = 0; ++ ++ dev_set_drvdata(dev, crisv10_hcd); ++ ++ devdrv_dbg("ETRAX USB IRQs HC:%d RX:%d TX:%d\n", ETRAX_USB_HC_IRQ, ++ ETRAX_USB_RX_IRQ, ETRAX_USB_TX_IRQ); ++ ++ /* Print out chip version read from registers */ ++ int rev_maj = *R_USB_REVISION & IO_MASK(R_USB_REVISION, major); ++ int rev_min = *R_USB_REVISION & IO_MASK(R_USB_REVISION, minor); ++ if(rev_min == 0) { ++ devdrv_info("Etrax 100LX USB Revision %d v1,2\n", rev_maj); ++ } else { ++ devdrv_info("Etrax 100LX USB Revision %d v%d\n", rev_maj, rev_min); ++ } ++ ++ devdrv_info("Bulk timer interval, start:%d eot:%d\n", ++ BULK_START_TIMER_INTERVAL, ++ BULK_EOT_TIMER_INTERVAL); ++ ++ ++ /* Init root hub data structures */ ++ if(rh_init()) { ++ devdrv_err("Failed init data for Root Hub\n"); ++ retval = -ENOMEM; ++ } ++ ++ if(port_in_use(0)) { ++ if (cris_request_io_interface(if_usb_1, "ETRAX100LX USB-HCD")) { ++ printk(KERN_CRIT "usb-host: request IO interface usb1 failed"); ++ retval = -EBUSY; ++ goto out; ++ } ++ devdrv_info("Claimed interface for USB physical port 1\n"); ++ } ++ if(port_in_use(1)) { ++ if (cris_request_io_interface(if_usb_2, "ETRAX100LX USB-HCD")) { ++ /* Free first interface if second failed to be claimed */ ++ if(port_in_use(0)) { ++ cris_free_io_interface(if_usb_1); ++ } ++ printk(KERN_CRIT "usb-host: request IO interface usb2 failed"); ++ retval = -EBUSY; ++ goto out; ++ } ++ devdrv_info("Claimed interface for USB physical port 2\n"); ++ } ++ ++ /* Init transfer controller structs and locks */ ++ if((retval = tc_init(hcd)) != 0) { ++ goto out; ++ } ++ ++ /* Attach interrupt functions for DMA and init DMA controller */ ++ if((retval = tc_dma_init(hcd)) != 0) { ++ goto out; ++ } ++ ++ /* Attach the top IRQ handler for USB controller interrupts */ ++ if (request_irq(ETRAX_USB_HC_IRQ, crisv10_hcd_top_irq, 0, ++ "ETRAX 100LX built-in USB (HC)", hcd)) { ++ err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ); ++ retval = -EBUSY; ++ goto out; ++ } ++ ++ /* iso_eof is only enabled when isoc traffic is running. */ ++ *R_USB_IRQ_MASK_SET = ++ /* IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) | */ ++ IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) | ++ IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) | ++ IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) | ++ IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set); ++ ++ ++ crisv10_ready_wait(); ++ /* 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); ++ ++ /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to ++ 0x2A30 (10800), to guarantee that control traffic gets 10% of the ++ bandwidth, and periodic transfer may allocate the rest (90%). ++ This doesn't work though. ++ The value 11960 is chosen to be just after the SOF token, with a couple ++ of bit times extra for possible bit stuffing. */ ++ *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960); ++ ++ crisv10_ready_wait(); ++ /* Configure the USB interface as a host controller. */ ++ *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_config); ++ ++ ++ /* Check so controller not busy before enabling ports */ ++ crisv10_ready_wait(); ++ ++ /* Enable selected USB ports */ ++ if(port_in_use(0)) { ++ *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); ++ } else { ++ *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes); ++ } ++ if(port_in_use(1)) { ++ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); ++ } else { ++ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes); ++ } ++ ++ 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); ++ ++ /* Do not continue probing initialization before USB interface is done */ ++ crisv10_ready_wait(); ++ ++ /* Register our Host Controller to USB Core ++ * Finish the remaining parts of generic HCD initialization: allocate the ++ * buffers of consistent memory, register the bus ++ * and call the driver's reset() and start() routines. */ ++ retval = usb_add_hcd(hcd, ETRAX_USB_HC_IRQ, IRQF_DISABLED); ++ if (retval != 0) { ++ devdrv_err("Failed registering HCD driver\n"); ++ goto out; ++ } ++ ++ return 0; ++ ++ out: ++ devdrv_hcd_remove(dev); ++ return retval; ++} ++ ++ ++/* cleanup after the host controller and driver */ ++static int __init_or_module devdrv_hcd_remove(struct device *dev) ++{ ++ struct crisv10_hcd *crisv10_hcd = dev_get_drvdata(dev); ++ struct usb_hcd *hcd; ++ ++ if (!crisv10_hcd) ++ return 0; ++ hcd = crisv10_hcd_to_hcd(crisv10_hcd); ++ ++ ++ /* Stop USB Controller in Etrax 100LX */ ++ crisv10_hcd_reset(hcd); ++ ++ usb_remove_hcd(hcd); ++ devdrv_dbg("Removed HCD from USB Core\n"); ++ ++ /* Free USB Controller IRQ */ ++ free_irq(ETRAX_USB_HC_IRQ, NULL); ++ ++ /* Free resources */ ++ tc_dma_destroy(); ++ tc_destroy(); ++ ++ ++ if(port_in_use(0)) { ++ cris_free_io_interface(if_usb_1); ++ } ++ if(port_in_use(1)) { ++ cris_free_io_interface(if_usb_2); ++ } ++ ++ devdrv_dbg("Freed all claimed resources\n"); ++ ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++ ++static int devdrv_hcd_suspend(struct usb_hcd *hcd, u32 state, u32 level) ++{ ++ return 0; /* no-op for now */ ++} ++ ++static int devdrv_hcd_resume(struct usb_hcd *hcd, u32 level) ++{ ++ return 0; /* no-op for now */ ++} ++ ++#endif /* CONFIG_PM */ ++ ++ ++ ++/*************************************************************/ ++/*************************************************************/ ++/* Module block */ ++/*************************************************************/ ++/*************************************************************/ ++ ++/* register driver */ ++static int __init module_hcd_init(void) ++{ ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ /* Here we select enabled ports by following defines created from ++ menuconfig */ ++#ifndef CONFIG_ETRAX_USB_HOST_PORT1 ++ ports &= ~(1<<0); ++#endif ++#ifndef CONFIG_ETRAX_USB_HOST_PORT2 ++ ports &= ~(1<<1); ++#endif ++ ++ printk(KERN_INFO "%s version "VERSION" "COPYRIGHT"\n", product_desc); ++ ++ devdrv_hc_platform_device = ++ platform_device_register_simple((char *) hc_name, 0, NULL, 0); ++ ++ if (IS_ERR(devdrv_hc_platform_device)) ++ return PTR_ERR(devdrv_hc_platform_device); ++ return driver_register(&devdrv_hc_device_driver); ++ /* ++ * Note that we do not set the DMA mask for the device, ++ * i.e. we pretend that we will use PIO, since no specific ++ * allocation routines are needed for DMA buffers. This will ++ * cause the HCD buffer allocation routines to fall back to ++ * kmalloc(). ++ */ ++} ++ ++/* unregister driver */ ++static void __exit module_hcd_exit(void) ++{ ++ driver_unregister(&devdrv_hc_device_driver); ++} ++ ++ ++/* Module hooks */ ++module_init(module_hcd_init); ++module_exit(module_hcd_exit); +diff -Nur linux-2.6.29.1.orig/drivers/usb/host/hc-crisv10.h linux-2.6.29.1/drivers/usb/host/hc-crisv10.h +--- linux-2.6.29.1.orig/drivers/usb/host/hc-crisv10.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.29.1/drivers/usb/host/hc-crisv10.h 2009-05-09 01:52:50.842184906 +0200 +@@ -0,0 +1,331 @@ ++#ifndef __LINUX_ETRAX_USB_H ++#define __LINUX_ETRAX_USB_H ++ ++#include <linux/types.h> ++#include <linux/list.h> ++ ++struct USB_IN_Desc { ++ volatile __u16 sw_len; ++ volatile __u16 command; ++ volatile unsigned long next; ++ volatile unsigned long buf; ++ volatile __u16 hw_len; ++ volatile __u16 status; ++}; ++ ++struct USB_SB_Desc { ++ volatile __u16 sw_len; ++ volatile __u16 command; ++ volatile unsigned long next; ++ volatile unsigned long buf; ++}; ++ ++struct USB_EP_Desc { ++ volatile __u16 hw_len; ++ volatile __u16 command; ++ volatile unsigned long sub; ++ volatile unsigned long next; ++}; ++ ++ ++/* Root Hub port status struct */ ++struct crisv10_rh { ++ volatile __u16 wPortChange[2]; ++ volatile __u16 wPortStatusPrev[2]; ++}; ++ ++/* HCD description */ ++struct crisv10_hcd { ++ spinlock_t lock; ++ __u8 num_ports; ++ __u8 running; ++}; ++ ++ ++/* Endpoint HC private data description */ ++struct crisv10_ep_priv { ++ int epid; ++}; ++ ++/* Additional software state info for a USB Controller epid */ ++struct etrax_epid { ++ __u8 inuse; /* !0 = setup in Etrax and used for a endpoint */ ++ __u8 disabled; /* !0 = Temporarly disabled to avoid resubmission */ ++ __u8 type; /* Setup as: PIPE_BULK, PIPE_CONTROL ... */ ++ __u8 out_traffic; /* !0 = This epid is for out traffic */ ++}; ++ ++/* Struct to hold information of scheduled later URB completion */ ++struct urb_later_data { ++// struct work_struct ws; ++ struct delayed_work ws; ++ struct usb_hcd *hcd; ++ struct urb *urb; ++ int urb_num; ++ int status; ++}; ++ ++ ++typedef enum { ++ STARTED, ++ NOT_STARTED, ++ UNLINK, ++} crisv10_urb_state_t; ++ ++ ++struct crisv10_urb_priv { ++ /* Sequence number for this URB. Every new submited URB gets this from ++ a incrementing counter. Used when a URB is scheduled for later finish to ++ be sure that the intended URB hasn't already been completed (device ++ drivers has a tendency to reuse URBs once they are completed, causing us ++ to not be able to single old ones out only based on the URB pointer.) */ ++ __u32 urb_num; ++ ++ /* The first_sb field is used for freeing all SB descriptors belonging ++ to an urb. The corresponding ep descriptor's sub pointer cannot be ++ used for this since the DMA advances the sub pointer as it processes ++ the sb list. */ ++ struct USB_SB_Desc *first_sb; ++ ++ /* The last_sb field referes to the last SB descriptor that belongs to ++ this urb. This is important to know so we can free the SB descriptors ++ that ranges between first_sb and last_sb. */ ++ struct USB_SB_Desc *last_sb; ++ ++ /* The rx_offset field is used in ctrl and bulk traffic to keep track ++ of the offset in the urb's transfer_buffer where incoming data should be ++ copied to. */ ++ __u32 rx_offset; ++ ++ /* Counter used in isochronous transfers to keep track of the ++ number of packets received/transmitted. */ ++ __u32 isoc_packet_counter; ++ ++ /* Flag that marks if this Isoc Out URB has finished it's transfer. Used ++ because several URBs can be finished before list is processed */ ++ __u8 isoc_out_done; ++ ++ /* This field is used to pass information about the urb's current state ++ between the various interrupt handlers (thus marked volatile). */ ++ volatile crisv10_urb_state_t urb_state; ++ ++ /* In Ctrl transfers consist of (at least) 3 packets: SETUP, IN and ZOUT. ++ When DMA8 sub-channel 2 has processed the SB list for this sequence we ++ get a interrupt. We also get a interrupt for In transfers and which ++ one of these interrupts that comes first depends of data size and device. ++ To be sure that we have got both interrupts before we complete the URB ++ we have these to flags that shows which part that has completed. ++ We can then check when we get one of the interrupts that if the other has ++ occured it's safe for us to complete the URB, otherwise we set appropriate ++ flag and do the completion when we get the other interrupt. */ ++ volatile unsigned char ctrl_zout_done; ++ volatile unsigned char ctrl_rx_done; ++ ++ /* Connection between the submitted urb and ETRAX epid number */ ++ __u8 epid; ++ ++ /* The rx_data_list field is used for periodic traffic, to hold ++ received data for later processing in the the complete_urb functions, ++ where the data us copied to the urb's transfer_buffer. Basically, we ++ use this intermediate storage because we don't know when it's safe to ++ reuse the transfer_buffer (FIXME?). */ ++ struct list_head rx_data_list; ++ ++ ++ /* The interval time rounded up to closest 2^N */ ++ int interval; ++ ++ /* Pool of EP descriptors needed if it's a INTR transfer. ++ Amount of EPs in pool correspons to how many INTR that should ++ be inserted in TxIntrEPList (max 128, defined by MAX_INTR_INTERVAL) */ ++ struct USB_EP_Desc* intr_ep_pool[128]; ++ ++ /* The mount of EPs allocated for this INTR URB */ ++ int intr_ep_pool_length; ++ ++ /* Pointer to info struct if URB is scheduled to be finished later */ ++ struct urb_later_data* later_data; ++}; ++ ++ ++/* This struct is for passing data from the top half to the bottom half irq ++ handlers */ ++struct crisv10_irq_reg { ++ struct usb_hcd* hcd; ++ __u32 r_usb_epid_attn; ++ __u8 r_usb_status; ++ __u16 r_usb_rh_port_status_1; ++ __u16 r_usb_rh_port_status_2; ++ __u32 r_usb_irq_mask_read; ++ __u32 r_usb_fm_number; ++ struct work_struct usb_bh; ++}; ++ ++ ++/* This struct is for passing data from the isoc top half to the isoc bottom ++ half. */ ++struct crisv10_isoc_complete_data { ++ struct usb_hcd *hcd; ++ struct urb *urb; ++ struct work_struct usb_bh; ++}; ++ ++/* Entry item for URB lists for each endpint */ ++typedef struct urb_entry ++{ ++ struct urb *urb; ++ struct list_head list; ++} urb_entry_t; ++ ++/* --------------------------------------------------------------------------- ++ Virtual Root HUB ++ ------------------------------------------------------------------------- */ ++/* 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 ++ ++/* Our Vendor Specific feature */ ++#define RH_REMOVE_EP 0x00 ++ ++ ++#define RH_ACK 0x01 ++#define RH_REQ_ERR -1 ++#define RH_NACK 0x00 ++ ++/* Field definitions for */ ++ ++#define USB_IN_command__eol__BITNR 0 /* command macros */ ++#define USB_IN_command__eol__WIDTH 1 ++#define USB_IN_command__eol__no 0 ++#define USB_IN_command__eol__yes 1 ++ ++#define USB_IN_command__intr__BITNR 3 ++#define USB_IN_command__intr__WIDTH 1 ++#define USB_IN_command__intr__no 0 ++#define USB_IN_command__intr__yes 1 ++ ++#define USB_IN_status__eop__BITNR 1 /* status macros. */ ++#define USB_IN_status__eop__WIDTH 1 ++#define USB_IN_status__eop__no 0 ++#define USB_IN_status__eop__yes 1 ++ ++#define USB_IN_status__eot__BITNR 5 ++#define USB_IN_status__eot__WIDTH 1 ++#define USB_IN_status__eot__no 0 ++#define USB_IN_status__eot__yes 1 ++ ++#define USB_IN_status__error__BITNR 6 ++#define USB_IN_status__error__WIDTH 1 ++#define USB_IN_status__error__no 0 ++#define USB_IN_status__error__yes 1 ++ ++#define USB_IN_status__nodata__BITNR 7 ++#define USB_IN_status__nodata__WIDTH 1 ++#define USB_IN_status__nodata__no 0 ++#define USB_IN_status__nodata__yes 1 ++ ++#define USB_IN_status__epid__BITNR 8 ++#define USB_IN_status__epid__WIDTH 5 ++ ++#define USB_EP_command__eol__BITNR 0 ++#define USB_EP_command__eol__WIDTH 1 ++#define USB_EP_command__eol__no 0 ++#define USB_EP_command__eol__yes 1 ++ ++#define USB_EP_command__eof__BITNR 1 ++#define USB_EP_command__eof__WIDTH 1 ++#define USB_EP_command__eof__no 0 ++#define USB_EP_command__eof__yes 1 ++ ++#define USB_EP_command__intr__BITNR 3 ++#define USB_EP_command__intr__WIDTH 1 ++#define USB_EP_command__intr__no 0 ++#define USB_EP_command__intr__yes 1 ++ ++#define USB_EP_command__enable__BITNR 4 ++#define USB_EP_command__enable__WIDTH 1 ++#define USB_EP_command__enable__no 0 ++#define USB_EP_command__enable__yes 1 ++ ++#define USB_EP_command__hw_valid__BITNR 5 ++#define USB_EP_command__hw_valid__WIDTH 1 ++#define USB_EP_command__hw_valid__no 0 ++#define USB_EP_command__hw_valid__yes 1 ++ ++#define USB_EP_command__epid__BITNR 8 ++#define USB_EP_command__epid__WIDTH 5 ++ ++#define USB_SB_command__eol__BITNR 0 /* command macros. */ ++#define USB_SB_command__eol__WIDTH 1 ++#define USB_SB_command__eol__no 0 ++#define USB_SB_command__eol__yes 1 ++ ++#define USB_SB_command__eot__BITNR 1 ++#define USB_SB_command__eot__WIDTH 1 ++#define USB_SB_command__eot__no 0 ++#define USB_SB_command__eot__yes 1 ++ ++#define USB_SB_command__intr__BITNR 3 ++#define USB_SB_command__intr__WIDTH 1 ++#define USB_SB_command__intr__no 0 ++#define USB_SB_command__intr__yes 1 ++ ++#define USB_SB_command__tt__BITNR 4 ++#define USB_SB_command__tt__WIDTH 2 ++#define USB_SB_command__tt__zout 0 ++#define USB_SB_command__tt__in 1 ++#define USB_SB_command__tt__out 2 ++#define USB_SB_command__tt__setup 3 ++ ++ ++#define USB_SB_command__rem__BITNR 8 ++#define USB_SB_command__rem__WIDTH 6 ++ ++#define USB_SB_command__full__BITNR 6 ++#define USB_SB_command__full__WIDTH 1 ++#define USB_SB_command__full__no 0 ++#define USB_SB_command__full__yes 1 ++ ++#endif +diff -Nur linux-2.6.29.1.orig/lib/klist.c linux-2.6.29.1/lib/klist.c +--- linux-2.6.29.1.orig/lib/klist.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/lib/klist.c 2009-05-09 01:52:50.846185695 +0200 +@@ -60,7 +60,7 @@ + { + knode->n_klist = klist; + /* no knode deserves to start its life dead */ +- WARN_ON(knode_dead(knode)); ++ //WARN_ON(knode_dead(knode)); + } + + static void knode_kill(struct klist_node *knode) diff --git a/target/foxboard/patches/mtd-root.patch b/target/foxboard/patches/mtd-root.patch new file mode 100644 index 000000000..3576848be --- /dev/null +++ b/target/foxboard/patches/mtd-root.patch @@ -0,0 +1,62 @@ +diff -Nur linux-2.6.29.1.orig/drivers/mtd/Kconfig linux-2.6.29.1/drivers/mtd/Kconfig +--- linux-2.6.29.1.orig/drivers/mtd/Kconfig 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/mtd/Kconfig 2009-05-02 19:24:14.444062164 +0200 +@@ -53,6 +53,11 @@ + should normally be compiled as kernel modules. The modules perform + various checks and verifications when loaded. + ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ depends on MTD_PARTITIONS ++ default y ++ + config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + depends on MTD_PARTITIONS +diff -Nur linux-2.6.29.1.orig/drivers/mtd/mtdpart.c linux-2.6.29.1/drivers/mtd/mtdpart.c +--- linux-2.6.29.1.orig/drivers/mtd/mtdpart.c 2009-04-02 22:55:27.000000000 +0200 ++++ linux-2.6.29.1/drivers/mtd/mtdpart.c 2009-05-02 19:26:39.038093851 +0200 +@@ -18,6 +18,7 @@ + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + #include <linux/mtd/compatmac.h> ++#include <linux/root_dev.h> + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -37,7 +38,7 @@ + * the pointer to that structure with this macro. + */ + #define PART(x) ((struct mtd_part *)(x)) +- ++#define IS_PART(mtd) (mtd->read == part_read) + + /* + * MTD methods which simply translate the effective address and pass through +@@ -502,14 +503,23 @@ + { + struct mtd_part *slave; + uint64_t cur_offset = 0; +- int i; ++ int i, j, ret; + + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + +- for (i = 0; i < nbparts; i++) { +- slave = add_one_partition(master, parts + i, i, cur_offset); ++ for (i = 0, j = 0; i < nbparts; i++) { ++ slave = add_one_partition(master, parts + i, j++, cur_offset); + if (!slave) + return -ENOMEM; ++ if (!strcmp(parts[i].name, "rootfs") && slave->registered) { ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); ++ } ++#endif ++ } + cur_offset = slave->offset + slave->mtd.size; + } + diff --git a/target/foxboard/tools/boot_linux b/target/foxboard/tools/boot_linux new file mode 100755 index 000000000..e7d5807f5 --- /dev/null +++ b/target/foxboard/tools/boot_linux @@ -0,0 +1,512 @@ +#!/usr/bin/perl -w + +#***************************************************************************** +#! +#! FILE NAME : boot_linux +#! +#! PARAMETERS : -b <bootimage> the name of the boot image to use +#! -d <device> the interface to use, e.g., eth1 +#! (defaults is eth0) +#! -f save it in flash memory at address 0x10000 +#! -F save it in flash memory at address 0 +#! -h show some help +#! -i <image> name of the image to use (default is fimage) +#! -o <offset> the offset in the flash where the flashing +#! starts +#! -O <offset> the offset in the image file where the +#! flashing starts from +#! -p print the resulting etrax100boot command +#! instead of executing it +#! -s <size> how much to flash (default is the size of +#! the flash minus the offset specified using +#! -o or -f) +#! -S <size> the size of the flash +#! +#! All sizes and offsets above can be specified as decimal +#! numbers, or as hexadecimal numbers by prefixing them with 0x. +#! It is also possible to use the suffixes k and M to specify +#! kilo (1024) or mega (1048576). +#! +#! DESCRIPTION: Extract the start of the image and any registers that should +#! be set from the kimage or fimage file, and then boot it. +#! +#! FUNCTIONS : convert_size +#! extract_hw_settings +#! get_dword +#! calculate_sdram_init +#! sdram_command +#! print_help +#! +#!---------------------------------------------------------------------------- +#! HISTORY +#! +#! $Log: boot_linux,v $ +#! Revision 1.16 2004/11/01 16:32:27 starvik +#! Corrected help text to avoid confusion +#! +#! Revision 1.15 2003/01/29 11:48:57 pkj +#! Calculate a flash size large enough for the given image if the +#! -S option is not specified. +#! +#! Revision 1.14 2002/11/18 14:40:09 pkj +#! Make use of the --loop option to etrax100boot when initialising +#! SDRAM memories. This requires a lot fewer options to be passed +#! to the boot loader. +#! +#! Revision 1.13 2002/08/15 16:29:02 pkj +#! * The -S option now accepts the size in bytes (just like the -s option). +#! For backwards compatibility it still assumes sizes of 16 and less to +#! be specified in MB. +#! * The suffixes k and M can now be used with all sizes and offsets to +#! specify them in kilo or mega. +#! +#! Revision 1.12 2002/08/15 15:27:34 pkj +#! Use $opts{'x'} instead of $opt_x. +#! +#! Revision 1.11 2002/07/04 17:06:39 pkj +#! * No longer specifies a bootfile by default (not needed any longer). +#! * Implemented option -b to specify a bootfile. +#! * Removed references to option -l (it was never implemented). +#! +#! Revision 1.10 2002/06/04 11:50:23 starvik +#! Check if mrs_data is specified in kernelconfig (necessary for MCM) +#! +#! Revision 1.9 2002/01/29 10:38:26 pkj +#! Change illegal to invalid. +#! +#! Revision 1.8 2001/09/13 12:32:10 pkj +#! * Added option -S to specify the size of the flash (in MB), as -s +#! is used to specify how much to flash nowadays. +#! * Made the default size of the flash depend on the size of the image +#! file. If it is bigger than 0x200100 then the flash is assumed to +#! be 4 MB, otherwise it is assumed to be 2 MB. +#! * Added verification of various options. +#! +#! Revision 1.7 2001/09/13 10:25:11 pkj +#! Minor clean-up. +#! +#! Revision 1.6 2001/06/29 10:05:16 pkj +#! Corrected check for SDRAM. +#! +#! Revision 1.5 2001/06/29 09:11:55 pkj +#! Synchronised boot_elinux and boot_linux. +#! +#!---------------------------------------------------------------------------- +#! (C) Copyright 2001, Axis Communications AB, LUND, SWEDEN +#!**************************************************************************** +# $Id: boot_linux,v 1.16 2004/11/01 16:32:27 starvik Exp $ + +#****************** INCLUDE FILES SECTION ************************************ + +use strict; + +use Getopt::Std; +use File::Basename; + +#****************** VARIABLE DECLARATION SECTION ***************************** + +use vars qw($my_name %opts); +use vars qw($text_start $cmd); +use vars qw($image_name $image_size); +use vars qw($offset $source_offset $flash_size $flashing_size); +use vars qw($sdram_timing_address $sdram_config_address); +use vars qw($sdram_precharge $sdram_nop $sdram_refresh $sdram_mrs); + +#****************** CONSTANT SECTION ***************************************** + +# Register addresses +$sdram_timing_address = "b0000008"; +$sdram_config_address = "b000000c"; + +# SDRAM commands +$sdram_precharge = 3; +$sdram_nop = 0; +$sdram_refresh = 2; +$sdram_mrs = 1; + +#****************** MAIN PROGRAM SECTION ************************************* + +# The name of this program. +$my_name = basename($0); + +# Get options +getopts('b:d:fFhi:o:O:ps:S:', \%opts); + +&print_help if ($opts{'h'}); + +# Name and existance of the image +$image_name = ($opts{'i'} ? $opts{'i'} : 'fimage'); +die "Could not find the image $image_name!\n" unless (-s $image_name); + +if ($opts{'f'} || $opts{'F'}) +{ + $image_size = -s $image_name; + + $offset = ($opts{'f'} ? 0x10000 : 0); + + $offset = &convert_size($opts{'o'}) if (defined($opts{'o'})); + + die("$my_name: Invalid destination offset\n") if ($offset !~ /^\d+$/); + + my $base_name = basename($image_name); + if ($base_name eq 'timage' || $base_name eq 'flash1.img') + { + $source_offset = 0; + } + else + { + $source_offset = $offset; + } + + $source_offset = &convert_size($opts{'O'}) if (defined($opts{'O'})); + + die("$my_name: Invalid source offset\n") if ($source_offset !~ /^\d+$/); + die("$my_name: Source offset > image size\n") if ($source_offset > $image_size); + + if (defined($opts{'S'})) + { + # Backwards compatibility to allow specifying the flash size in MB + # without using an M suffix + $opts{'S'} .= 'M' if ($opts{'S'} =~ /^\d+$/ && $opts{'S'} <= 16); + + $flash_size = &convert_size($opts{'S'}); + } + else + { + # Calculate a flash size large enough for the image without the checksum + # and HWID. + $flash_size = ($image_size - $source_offset + $offset) & 0xFFFF0000; + } + + die("$my_name: Invalid flash size\n") if ($flash_size !~ /^\d+$/); + die("$my_name: Destination offset > flash size\n") if ($offset > $flash_size); + if (defined($opts{'s'})) + { + $flashing_size = &convert_size($opts{'s'}); + } + else + { + $flashing_size = $flash_size - $offset; + } + + die("$my_name: Invalid size to flash\n") if ($flashing_size !~ /^\d+$/); + + if ($flashing_size > $flash_size - $offset) + { + $flashing_size = $flash_size - $offset; + printf("Warning: Flashing size limited to 0x%lx due to the offset (0x%lx) and flash size (0x%lx).\n", $flashing_size, $offset, $flash_size); + } + + if ($flashing_size > $image_size - $source_offset) + { + $flashing_size = $image_size - $source_offset; + printf("Warning: Flashing size limited to 0x%lx due to the offset (0x%lx) and image size (0x%lx).\n", $flashing_size, $source_offset, $image_size); + } +} + +# Create the command line to boot the image +if (system('./etrax100boot --help > /dev/null') == 0) +{ + $cmd = './etrax100boot'; +} +elsif (system('svinto_boot --help > /dev/null') == 0) +{ + $cmd = 'svinto_boot'; +} +else +{ + die("Cannot find e100boot program in your PATH!\n"); +} + +$cmd .= " --device $opts{'d'}" if ($opts{'d'}); + +$cmd .= &extract_hw_settings; + +$cmd .= " --bootfile $opts{'b'}" if ($opts{'b'}); +$cmd .= " --file $image_name $text_start"; + +if ($opts{'f'} || $opts{'F'}) +{ + $cmd .= sprintf(" --flash %lx %lx %lx --jump 0", + hex($text_start) + $source_offset, $offset, $flashing_size); +} +else +{ + $cmd .= " --jump $text_start"; +} + +if ($opts{'p'}) +{ + print "Command:\n$cmd\n"; +} +else +{ + system($cmd); +} + +exit 0; + +#****************** FUNCTION DEFINITION SECTION ****************************** + +#***************************************************************************** +## +## FUNCTION NAME: convert_size +## +##**************************************************************************** + +sub convert_size +{ + my($arg) = @_; + my $size; + + if ($arg =~ /^0x([\da-fA-F]+)([kM])?$/) + { + $size = hex($1); + } + elsif ($arg =~ /^(\d+)([kM])?$/) + { + $size = $1; + } + else + { + return -1; + } + + if (!defined($2)) + { + return $size; + } + elsif ($2 eq 'k') + { + return $size * 1024; + } + elsif ($2 eq 'M') + { + return $size * 1048576; + } +} + +#***************************************************************************** +## +## FUNCTION NAME: extract_hw_settings +## +##**************************************************************************** + +sub extract_hw_settings +{ + my $data; + my $dbg_port; + my $sdram_enabled; + my $return_value = ""; + my $sdram_config; + + # The hw information table has the following format + # + # "HW_PARAM_MAGIC" + # text_start (dword) + # serial debg port (dword) + # sdram enabled (dword) + # register address (dword) + # register value (dword) + # ... + # 0 + + open(FILE, "$image_name") || die("Could not open '$image_name'"); + + while (<FILE>) + { + if (m/HW_PARAM_MAGIC/g) + { + # Seek to first byte after magic + seek(FILE, -length($_) + pos($_), 1); + last; + } + } + + $text_start = &get_dword; + $dbg_port = &get_dword; + $sdram_enabled = int(&get_dword); + + while (1) + { + my $register = &get_dword; + my $value = &get_dword; + + last if ($register eq "00000000"); + + if ($sdram_enabled) + { + if ($register eq $sdram_config_address) + { + $sdram_config = $value; + } + elsif ($register eq $sdram_timing_address) + { + $return_value .= &calculate_sdram_init($value, $sdram_config); + next; + } + } + + $return_value .= " --setreg $register $value"; + } + + close(FILE); + + return $return_value; +} + +#***************************************************************************** +## +## FUNCTION NAME: get_dword +## +##**************************************************************************** + +sub get_dword +{ + my $data; + + read(FILE, $data, 4); + return unpack("H8", pack("V", unpack("N", $data))); +} + +#***************************************************************************** +## +## FUNCTION NAME: calculate_sdram_init +## +##**************************************************************************** + +sub calculate_sdram_init +{ + # Refer to ETRAX 100LX Designers Reference for a description of SDRAM + # initialization + my $sdram_init_val = hex($_[0]); + my $sdram_config_val = hex($_[1]); + my $bus_width = $sdram_config_val & 0x00800000; + my $speed; + my $cas_latency; + my $mrs_data; + my $temp; + my $return_value; + my $value; + + $mrs_data = ($sdram_init_val & 0x00ff0000) >> 16; + $sdram_init_val &= 0x8000ffff; # Make sure mrs data is 0 + $sdram_init_val |= 0x80000000; # Make sure sdram is enabled + $speed = $sdram_init_val & 0x1000; + $cas_latency = $sdram_init_val & 0x3; + if ($speed) # 100 MHz + { + $cas_latency += 2; + } + else # 50 MHz + { + $cas_latency += 1; + } + + # Calculate value of mrs_data + # CAS latency = 2 && bus_width = 32 => 0x40 + # CAS latency = 3 && bus_width = 32 => 0x60 + # CAS latency = 2 && bus_width = 16 => 0x20 + # CAS latency = 3 && bus_width = 16 => 0x30 + if ($mrs_data == 0) + { + if ($bus_width == 0) # 16 bits + { + $mrs_data = $cas_latency == 2 ? 0x20 : 0x30; + } + else # 32 bits + { + $mrs_data = $cas_latency == 2 ? 0x40 : 0x60; + } + } + + $temp = $sdram_init_val | 0x0000c000; # Disable refresh + $return_value .= &sdram_command($temp); + $return_value .= " --pause 20000"; + + $return_value .= &sdram_command($temp, $sdram_precharge); + $return_value .= &sdram_command($temp, $sdram_nop); + + $return_value .= " --setreg +0 7"; + $return_value .= " --label label1"; + $return_value .= &sdram_command($temp, $sdram_refresh); + $return_value .= &sdram_command($temp, $sdram_nop); + $return_value .= " --loop +0 label1"; + + $return_value .= &sdram_command($temp, $sdram_mrs, $mrs_data); + $return_value .= &sdram_command($temp, $sdram_nop); + + $return_value .= &sdram_command($sdram_init_val); + + return $return_value; +} + +#***************************************************************************** +## +## FUNCTION NAME: sdram_command +## +##**************************************************************************** + +sub sdram_command +{ + my($temp, $value, $mrs_data) = @_; + + $value ||= 0; + if ($value == $sdram_mrs) + { + $value = sprintf("%lx", $temp | ($value << 9) | ($mrs_data << 16)); + } + else + { + $value = sprintf("%lx", $temp | ($value << 9)); + } + + return " --setreg $sdram_timing_address $value"; +} + +#***************************************************************************** +## +## FUNCTION NAME: print_help +## +##**************************************************************************** + +sub print_help +{ + print "\nAXIS $my_name, ", '$Revision: 1.16 $ $Date: 2004/11/01 16:32:27 $ ', "\n"; + die <<EOT; +Copyright (C) 2001-2002 Axis Communications AB + +DESCRIPTION: + This program is used to boot (and flash) a linux image to a box. + It tries to extract the required ETRAX 100 settings from the image file. + +SYNTAX: + $my_name [options] + +OPTIONS: + -b <bootfile> : The boot image to use. + -d <device> : The network interface to use, default is eth0. + -f : Save the image in the flash memory starting at + address 0x10000. + -F : Save the image in the flash memory starting at + address 0. + -h : Print this help text. + -i <image> : The path and name of the image to use, default + is fimage. + -o <offset> : The offset in the flash where the flashing starts. + -O <offset> : The offset in the image file where the flashing + starts from. + -p : Print the resulting etrax100boot command instead + of executing it. + -s <size> : How much to flash (default is the size of the + flash minus the offset specified using -o or -f). + -S <size> : The size of the flash. + + All sizes and offsets above can be specified as decimal numbers, or as + hexadecimal numbers by prefixing them with 0x. It is also possible to use + the suffixes k and M to specify kilo (1024) or mega (1048576). + +EOT +} + +#****************** END OF FILE boot_linux *********************************** diff --git a/target/foxboard/tools/e100boot/Makefile b/target/foxboard/tools/e100boot/Makefile new file mode 100644 index 000000000..9e9f78861 --- /dev/null +++ b/target/foxboard/tools/e100boot/Makefile @@ -0,0 +1,26 @@ +# $Id$ +#- +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include $(TOPDIR)/rules.mk + +PKG_NAME:= e100boot +PKG_VERSION:= 0.1 +PKG_RELEASE:= 1 +PKG_MD5SUM:= 11fd53e7824dc550e4cffbc4cd114c3e +MASTER_SITES:= http://download.tuxfamily.org/crisos/repository/source/ +DISTFILES:= ${PKG_NAME}.tar.bz2 +WRKDIST= ${WRKDIR}/${PKG_NAME} + +include ../rules.mk + +$(WRKBUILD)/.compiled: ${WRKDIST}/.prepared + $(MAKE) -C $(WRKBUILD) + touch $@ + +$(WRKBUILD)/.installed: $(WRKBUILD)/.compiled + $(INSTALL_BIN) $(WRKBUILD)/sbl/e100boot.stripped $(BIN_DIR)/etrax100boot + touch $@ + +include $(TOPDIR)/mk/tools.mk diff --git a/target/foxboard/tools/mkfimage/Makefile b/target/foxboard/tools/mkfimage/Makefile new file mode 100644 index 000000000..5aae0914f --- /dev/null +++ b/target/foxboard/tools/mkfimage/Makefile @@ -0,0 +1,4 @@ +include $(TOPDIR)/rules.mk + +all: + $(HOSTCC) -o ${STAGING_TOOLS}/bin/mkfimage mkfimage.c diff --git a/target/foxboard/tools/mkfimage/mkfimage b/target/foxboard/tools/mkfimage/mkfimage Binary files differnew file mode 100755 index 000000000..b4f82ee6d --- /dev/null +++ b/target/foxboard/tools/mkfimage/mkfimage diff --git a/target/foxboard/tools/mkfimage/mkfimage.c b/target/foxboard/tools/mkfimage/mkfimage.c new file mode 100644 index 000000000..6904170cf --- /dev/null +++ b/target/foxboard/tools/mkfimage/mkfimage.c @@ -0,0 +1,72 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +int main(int argc, char **argv){ + unsigned char *buffer = malloc(64 * 1024); + struct stat s; + unsigned int size_vmlinux = 0, real_size_vmlinux = 0; + const unsigned char *magic_str = "ACME_PART_MAGIC"; + unsigned int loop; + unsigned char *magic; + + if(argc != 3){ + printf("%s in out\n", argv[0]); + return 1; + } + + printf("Generating image\n"); + + FILE *vmlinux = fopen(argv[1], "r"); + FILE *vmlinux_out = fopen(argv[2], "w"); + if((!vmlinux) || (!vmlinux_out)){ + printf("Error opening a file\n"); + return 1; + } + + stat(argv[1], &s); + size_vmlinux = s.st_size; + real_size_vmlinux = (size_vmlinux & 0xffff0000) + 0x10000; + + printf("vmlinux = 0x%.08X / 0x%.08X\n", size_vmlinux, real_size_vmlinux); + + unsigned int t = fread(buffer, 1, 64 * 1024, vmlinux); + for(loop = 0; loop < (64 * 1024) - sizeof(magic_str); loop++){ + if(buffer[loop] == magic_str[0]){ + if((magic = strstr(&buffer[loop], magic_str))){ + printf("Magic at 0x%.08X %p %p\n", magic - buffer, magic, buffer); + printf("Found Magic %X%X%X%X\n", + buffer[loop + strlen(magic_str)], + buffer[loop + strlen(magic_str) + 2], + buffer[loop + strlen(magic_str) + 1], + buffer[loop + strlen(magic_str) + 3]); + + buffer[loop + strlen(magic_str)] = real_size_vmlinux >> 24; + buffer[loop + strlen(magic_str) + 2] = (real_size_vmlinux >> 16) & 0xff; + buffer[loop + strlen(magic_str) + 1] = (real_size_vmlinux >> 8) & 0xff; + buffer[loop + strlen(magic_str) + 3] = (real_size_vmlinux) & 0xff; + + printf("Replaced with %.02X%.02X%.02X%.02X\n", + buffer[loop + strlen(magic_str)], + buffer[loop + strlen(magic_str) + 2], + buffer[loop + strlen(magic_str) + 1], + buffer[loop + strlen(magic_str) + 3]); + + } + } + } + + fwrite(buffer, 1, 64 * 1024, vmlinux_out); + real_size_vmlinux -= 64 * 1024; + do { + real_size_vmlinux -= 64 * 1024; + memset(buffer, 0, 64 * 1024); + fread(buffer, 1, 64 * 1024, vmlinux); + fwrite(buffer, 1, 64 * 1024, vmlinux_out); + } while (real_size_vmlinux); + + return 0; +} diff --git a/target/foxboard/tools/rules.mk b/target/foxboard/tools/rules.mk new file mode 100644 index 000000000..8d03c031a --- /dev/null +++ b/target/foxboard/tools/rules.mk @@ -0,0 +1,9 @@ +# $Id$ +#- +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +WRKDIR_BASE= ${TOOLS_BUILD_DIR} +WRKDIR= ${WRKDIR_BASE} + +include ${TOPDIR}/mk/buildhlp.mk diff --git a/target/foxboard/tools/squashfs/Makefile b/target/foxboard/tools/squashfs/Makefile new file mode 100644 index 000000000..866beabf5 --- /dev/null +++ b/target/foxboard/tools/squashfs/Makefile @@ -0,0 +1,27 @@ +# $Id$ +#- +# This file is part of the OpenADK project. OpenADK is copyrighted +# material, please see the LICENCE file in the top-level directory. + +include $(TOPDIR)/rules.mk + +PKG_NAME:= squashfs +PKG_VERSION:= 4.0 +PKG_RELEASE:= 1 +PKG_MD5SUM:= a3c23391da4ebab0ac4a75021ddabf96 +MASTER_SITES:= ${MASTER_SITE_SOURCEFORGE:=squashfs/} +DISTFILES:= ${PKG_NAME}${PKG_VERSION}.tar.gz +WRKDIST= ${WRKDIR}/$(PKG_NAME)${PKG_VERSION} + +include ../rules.mk + +$(WRKBUILD)/.compiled: ${WRKDIST}/.prepared + $(MAKE) -C $(WRKBUILD)/squashfs-tools + touch $@ + +$(WRKBUILD)/.installed: + $(INSTALL_BIN) $(WRKBUILD)/squashfs-tools/mksquashfs \ + ${STAGING_TOOLS}/bin + touch $@ + +include $(TOPDIR)/mk/tools.mk diff --git a/target/foxboard/uclibc.config b/target/foxboard/uclibc.config new file mode 100644 index 000000000..19c3e431f --- /dev/null +++ b/target/foxboard/uclibc.config @@ -0,0 +1,241 @@ +# +# Automatically generated make config: don't edit +# Version: 0.9.30 +# Thu Dec 25 10:29:25 2008 +# +# TARGET_alpha is not set +# TARGET_arm is not set +# TARGET_avr32 is not set +# TARGET_bfin is not set +TARGET_cris=y +# TARGET_e1 is not set +# TARGET_frv is not set +# TARGET_h8300 is not set +# TARGET_hppa is not set +# TARGET_i386 is not set +# TARGET_i960 is not set +# TARGET_ia64 is not set +# TARGET_m68k is not set +# TARGET_microblaze is not set +# TARGET_mips is not set +# TARGET_nios is not set +# TARGET_nios2 is not set +# TARGET_powerpc is not set +# TARGET_sh is not set +# TARGET_sh64 is not set +# TARGET_sparc is not set +# TARGET_v850 is not set +# TARGET_vax is not set +# TARGET_x86_64 is not set +# TARGET_xtensa is not set + +# +# Target Architecture Features and Options +# +TARGET_ARCH="cris" +FORCE_OPTIONS_FOR_ARCH=y +CONFIG_CRIS=y +# CONFIG_CRISV32 is not set +# CONFIG_GENERIC_386 is not set +# CONFIG_386 is not set +# CONFIG_486 is not set +# CONFIG_586 is not set +# CONFIG_586MMX is not set +# CONFIG_686 is not set +# CONFIG_PENTIUMII is not set +# CONFIG_PENTIUMIII is not set +# CONFIG_PENTIUM4 is not set +# CONFIG_K6 is not set +# CONFIG_K7 is not set +# CONFIG_ELAN is not set +# CONFIG_CRUSOE is not set +# CONFIG_WINCHIPC6 is not set +# CONFIG_WINCHIP2 is not set +# CONFIG_CYRIXIII is not set +# CONFIG_NEHEMIAH is not set +TARGET_SUBARCH="" + +# +# Using ELF file format +# +ARCH_LITTLE_ENDIAN=y + +# +# Using Little Endian +# +ARCH_HAS_MMU=y +ARCH_USE_MMU=y +UCLIBC_HAS_FLOATS=y +UCLIBC_HAS_FPU=y +DO_C99_MATH=y +UCLIBC_HAS_FENV=y +KERNEL_HEADERS="" +HAVE_DOT_CONFIG=y + +# +# General Library Settings +# +# HAVE_NO_PIC is not set +DOPIC=y +# ARCH_HAS_NO_SHARED is not set +# ARCH_HAS_NO_LDSO is not set +HAVE_SHARED=y +# FORCE_SHAREABLE_TEXT_SEGMENTS is not set +LDSO_LDD_SUPPORT=y +LDSO_CACHE_SUPPORT=y +# LDSO_PRELOAD_FILE_SUPPORT is not set +LDSO_BASE_FILENAME="ld.so" +# UCLIBC_STATIC_LDCONFIG is not set +LDSO_RUNPATH=y +UCLIBC_CTOR_DTOR=y +# LDSO_GNU_HASH_SUPPORT is not set +# HAS_NO_THREADS is not set +UCLIBC_HAS_THREADS=y +PTHREADS_DEBUG_SUPPORT=y +LINUXTHREADS_OLD=y +UCLIBC_HAS_SYSLOG=y +UCLIBC_HAS_LFS=y +# MALLOC is not set +# MALLOC_SIMPLE is not set +MALLOC_STANDARD=y +MALLOC_GLIBC_COMPAT=y +UCLIBC_DYNAMIC_ATEXIT=y +COMPAT_ATEXIT=y +# UCLIBC_SUSV3_LEGACY is not set +# UCLIBC_SUSV3_LEGACY_MACROS is not set +# UCLIBC_HAS_STUBS is not set +UCLIBC_HAS_SHADOW=y +UCLIBC_HAS_PROGRAM_INVOCATION_NAME=y +UCLIBC_HAS___PROGNAME=y +UCLIBC_HAS_PTY=y +ASSUME_DEVPTS=y +UNIX98PTY_ONLY=y +# UCLIBC_HAS_GETPT is not set +UCLIBC_HAS_TM_EXTENSIONS=y +UCLIBC_HAS_TZ_CACHING=y +UCLIBC_HAS_TZ_FILE=y +UCLIBC_HAS_TZ_FILE_READ_MANY=y +UCLIBC_TZ_FILE_PATH="/etc/TZ" + +# +# Advanced Library Settings +# +UCLIBC_PWD_BUFFER_SIZE=256 +UCLIBC_GRP_BUFFER_SIZE=256 + +# +# Support various families of functions +# +# UCLIBC_LINUX_MODULE_24 is not set +UCLIBC_LINUX_SPECIFIC=y +UCLIBC_HAS_GNU_ERROR=y +UCLIBC_BSD_SPECIFIC=y +UCLIBC_HAS_BSD_ERR=y +# UCLIBC_HAS_OBSOLETE_BSD_SIGNAL is not set +# UCLIBC_HAS_OBSOLETE_SYSV_SIGNAL is not set +# UCLIBC_NTP_LEGACY is not set +# UCLIBC_SV4_DEPRECATED is not set +UCLIBC_HAS_REALTIME=y +# UCLIBC_HAS_ADVANCED_REALTIME is not set +UCLIBC_HAS_EPOLL=y +UCLIBC_HAS_XATTR=y +# UCLIBC_HAS_PROFILING is not set +UCLIBC_HAS_CRYPT_IMPL=y +UCLIBC_HAS_CRYPT=y +UCLIBC_HAS_NETWORK_SUPPORT=y +UCLIBC_HAS_SOCKET=y +UCLIBC_HAS_IPV4=y +# UCLIBC_HAS_IPV6 is not set +UCLIBC_HAS_RPC=y +UCLIBC_HAS_FULL_RPC=y +UCLIBC_HAS_REENTRANT_RPC=y +UCLIBC_USE_NETLINK=y +UCLIBC_SUPPORT_AI_ADDRCONFIG=y +UCLIBC_HAS_BSD_RES_CLOSE=y + +# +# String and Stdio Support +# +UCLIBC_HAS_STRING_GENERIC_OPT=y +UCLIBC_HAS_STRING_ARCH_OPT=y +UCLIBC_HAS_CTYPE_TABLES=y +UCLIBC_HAS_CTYPE_SIGNED=y +# UCLIBC_HAS_CTYPE_UNSAFE is not set +UCLIBC_HAS_CTYPE_CHECKED=y +# UCLIBC_HAS_CTYPE_ENFORCED is not set +UCLIBC_HAS_WCHAR=y +# UCLIBC_HAS_LOCALE is not set +UCLIBC_HAS_HEXADECIMAL_FLOATS=y +UCLIBC_HAS_GLIBC_CUSTOM_PRINTF=y +UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS=9 +UCLIBC_HAS_SCANF_GLIBC_A_FLAG=y +# UCLIBC_HAS_STDIO_BUFSIZ_NONE is not set +# UCLIBC_HAS_STDIO_BUFSIZ_256 is not set +# UCLIBC_HAS_STDIO_BUFSIZ_512 is not set +# UCLIBC_HAS_STDIO_BUFSIZ_1024 is not set +# UCLIBC_HAS_STDIO_BUFSIZ_2048 is not set +UCLIBC_HAS_STDIO_BUFSIZ_4096=y +# UCLIBC_HAS_STDIO_BUFSIZ_8192 is not set +UCLIBC_HAS_STDIO_BUILTIN_BUFFER_NONE=y +# UCLIBC_HAS_STDIO_BUILTIN_BUFFER_4 is not set +# UCLIBC_HAS_STDIO_BUILTIN_BUFFER_8 is not set +# UCLIBC_HAS_STDIO_SHUTDOWN_ON_ABORT is not set +UCLIBC_HAS_STDIO_GETC_MACRO=y +UCLIBC_HAS_STDIO_PUTC_MACRO=y +UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION=y +# UCLIBC_HAS_FOPEN_LARGEFILE_MODE is not set +UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE=y +UCLIBC_HAS_GLIBC_CUSTOM_STREAMS=y +UCLIBC_HAS_PRINTF_M_SPEC=y +UCLIBC_HAS_ERRNO_MESSAGES=y +# UCLIBC_HAS_SYS_ERRLIST is not set +UCLIBC_HAS_SIGNUM_MESSAGES=y +# UCLIBC_HAS_SYS_SIGLIST is not set +UCLIBC_HAS_GNU_GETOPT=y +UCLIBC_HAS_GNU_GETSUBOPT=y + +# +# Big and Tall +# +UCLIBC_HAS_REGEX=y +UCLIBC_HAS_REGEX_OLD=y +UCLIBC_HAS_FNMATCH=y +UCLIBC_HAS_FNMATCH_OLD=y +UCLIBC_HAS_WORDEXP=y +UCLIBC_HAS_FTW=y +UCLIBC_HAS_GLOB=y +UCLIBC_HAS_GNU_GLOB=y + +# +# Library Installation Options +# +SHARED_LIB_LOADER_PREFIX="/lib" +RUNTIME_PREFIX="/" +DEVEL_PREFIX="/usr/" + +# +# Security options +# +UCLIBC_HAS_ARC4RANDOM=y +# HAVE_NO_SSP is not set +# UCLIBC_HAS_SSP is not set +UCLIBC_BUILD_RELRO=y +# UCLIBC_BUILD_NOW is not set +UCLIBC_BUILD_NOEXECSTACK=y + +# +# uClibc development/debugging options +# +CROSS_COMPILER_PREFIX="" +UCLIBC_EXTRA_CFLAGS="" +# DODEBUG is not set +# DODEBUG_PT is not set +# DOASSERTS is not set +DOSTRIP=y +# SUPPORT_LD_DEBUG is not set +# SUPPORT_LD_DEBUG_EARLY is not set +# UCLIBC_MALLOC_DEBUGGING is not set +WARNINGS="-Wall" +EXTRA_WARNINGS=y +# DOMULTI is not set +# UCLIBC_MJN3_ONLY is not set |