diff -urN linux-2.6.19.2.old/arch/cris/Kconfig linux-2.6.19.2.dev/arch/cris/Kconfig --- linux-2.6.19.2.old/arch/cris/Kconfig 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/Kconfig 2007-01-17 18:17:18.000000000 +0100 @@ -16,6 +16,10 @@ config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_IOMAP + bool + default y + config GENERIC_FIND_NEXT_BIT bool default y @@ -42,6 +46,29 @@ source "fs/Kconfig.binfmt" +config GENERIC_HARDIRQS + bool + default y + +config SMP + bool "SMP" + help + SMP support. Always Say N. + +config NR_CPUS + int + depends on SMP + default 2 + +config SCHED_MC + bool "Multi-core scheduler support" + depends on SMP + default y + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + config ETRAX_CMDLINE string "Kernel command line" default "root=/dev/mtdblock3" @@ -70,17 +97,11 @@ timers). This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled. -config PREEMPT - bool "Preemptible Kernel" - help - This option reduces the latency of the kernel when reacting to - real-time or interactive events by allowing a low priority process to - be preempted even if it is in kernel mode executing a system call. - This allows applications to run more reliably even when the system is - under load. +config OOM_REBOOT + bool "Enable reboot at out of memory" - Say Y here if you are building a kernel for a desktop, embedded - or real-time system. Say N if you are unsure. +source "kernel/Kconfig.preempt" +source "kernel/Kconfig.sched" source mm/Kconfig @@ -107,6 +128,16 @@ help Support the xsim ETRAX Simulator. +config ETRAXFS + bool "ETRAX-FS-V32" + help + Support CRIS V32. + +config ETRAXFS_SIM + bool "ETRAX-FS-V32-Simulator" + help + Support CRIS V32 VCS simualtor. + endchoice config ETRAX_ARCH_V10 @@ -114,6 +145,11 @@ default y if ETRAX100LX || ETRAX100LX_V2 default n if !(ETRAX100LX || ETRAX100LX_V2) +config ETRAX_ARCH_V32 + bool + default y if ETRAXFS || ETRAXFS_SIM + default n if !(ETRAXFS || ETRAXFS_SIM) + config ETRAX_DRAM_SIZE int "DRAM size (dec, in MB)" default "8" @@ -121,12 +157,23 @@ Size of DRAM (decimal in MB) typically 2, 8 or 16. config ETRAX_FLASH_BUSWIDTH - int "Buswidth of flash in bytes" + int "Buswidth of NOR flash in bytes" default "2" help - Width in bytes of the Flash bus (1, 2 or 4). Is usually 2. + Width in bytes of the NOR Flash bus (1, 2 or 4). Is usually 2. -source arch/cris/arch-v10/Kconfig +config ETRAX_NANDFLASH_BUSWIDTH + int "Buswidth of NAND flash in bytes" + default "1" + help + Width in bytes of the NAND flash (1 or 2). + +config ETRAX_FLASH1_SIZE + int "FLASH1 size (dec, in MB. 0 = Unknown)" + default "0" + +# arch/cris/arch is a symlink to correct arch (arch-v10 or arch-v32) +source arch/cris/arch/Kconfig endmenu @@ -134,7 +181,8 @@ # bring in ETRAX built-in drivers menu "Drivers for built-in interfaces" -source arch/cris/arch-v10/drivers/Kconfig +# arch/cris/arch is a symlink to correct arch (arch-v10 or arch-v32) +source arch/cris/arch/drivers/Kconfig endmenu @@ -181,6 +229,10 @@ source "sound/Kconfig" +source "drivers/pcmcia/Kconfig" + +source "drivers/pci/Kconfig" + source "drivers/usb/Kconfig" source "arch/cris/Kconfig.debug" diff -urN linux-2.6.19.2.old/arch/cris/Kconfig.debug linux-2.6.19.2.dev/arch/cris/Kconfig.debug --- linux-2.6.19.2.old/arch/cris/Kconfig.debug 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/Kconfig.debug 2005-10-31 09:48:02.000000000 +0100 @@ -1,14 +1,15 @@ menu "Kernel hacking" +source "lib/Kconfig.debug" + #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC + config PROFILING bool "Kernel profiling support" config SYSTEM_PROFILER bool "System profiling support" -source "lib/Kconfig.debug" - config ETRAX_KGDB bool "Use kernel GDB debugger" depends on DEBUG_KERNEL diff -urN linux-2.6.19.2.old/arch/cris/Makefile linux-2.6.19.2.dev/arch/cris/Makefile --- linux-2.6.19.2.old/arch/cris/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/Makefile 2006-11-29 17:05:40.000000000 +0100 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $ +# $Id: Makefile,v 1.41 2006/11/29 16:05:40 ricardw Exp $ # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -10,14 +10,11 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. -# A bug in ld prevents us from having a (constant-value) symbol in a -# "ORIGIN =" or "LENGTH =" expression. - arch-y := v10 arch-$(CONFIG_ETRAX_ARCH_V10) := v10 arch-$(CONFIG_ETRAX_ARCH_V32) := v32 -# No config avaiable for make clean etc +# No config available for make clean etc ifneq ($(arch-y),) SARCH := arch-$(arch-y) else @@ -52,79 +49,58 @@ # cris object files path OBJ_ARCH = $(objtree)/arch/$(ARCH) -target_boot_arch_dir = $(OBJ_ARCH)/$(SARCH)/boot -target_boot_dir = $(OBJ_ARCH)/boot -src_boot_dir = $(SRC_ARCH)/boot -target_compressed_dir = $(OBJ_ARCH)/boot/compressed -src_compressed_dir = $(SRC_ARCH)/boot/compressed -target_rescue_dir = $(OBJ_ARCH)/boot/rescue -src_rescue_dir = $(SRC_ARCH)/boot/rescue - -export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir - -vmlinux.bin: vmlinux - $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin - -timage: vmlinux.bin - cat vmlinux.bin cramfs.img >timage - -simimage: timage - cp vmlinux.bin simvmlinux.bin - -# the following will remake timage without compiling the kernel -# it does of course require that all object files exist... - -cramfs: -## cramfs - Creates a cramfs image - mkcramfs -b 8192 -m romfs_meta.txt root cramfs.img - cat vmlinux.bin cramfs.img >timage - -clinux: vmlinux.bin decompress.bin rescue.bin - -decompress.bin: $(target_boot_dir) - @$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin - -$(target_rescue_dir)/rescue.bin: $(target_boot_dir) - @$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin +boot := arch/$(ARCH)/boot +MACHINE := arch/$(ARCH)/$(SARCH) -zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin -## zImage - Compressed kernel (gzip) - @$(MAKE) -f $(src_boot_dir)/Makefile zImage +all: zImage -$(target_boot_dir): $(target_boot_arch_dir) - ln -sfn $< $@ - -$(target_boot_arch_dir): - mkdir -p $@ - -compressed: zImage - -archmrproper: -archclean: - @if [ -d arch/$(ARCH)/boot ]; then \ - $(MAKE) $(clean)=arch/$(ARCH)/boot ; \ - fi - rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img - rm -rf $(LD_SCRIPT).tmp +zImage Image: vmlinux + $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch # Create some links to make all tools happy $(SRC_ARCH)/.links: @rm -rf $(SRC_ARCH)/drivers - @ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers + @ln -sfn $(SARCH)/drivers $(SRC_ARCH)/drivers @rm -rf $(SRC_ARCH)/boot - @ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot + @ln -sfn $(SARCH)/boot $(SRC_ARCH)/boot @rm -rf $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch - @ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S - @ln -sfn $(SRC_ARCH)/$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn $(SARCH)/lib $(SRC_ARCH)/lib + @rm -rf $(SRC_ARCH)/arch + @ln -sfn $(SARCH) $(SRC_ARCH)/arch + @rm -rf $(SRC_ARCH)/kernel/vmlinux.lds.S + @ln -sfn ../$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S + @rm -rf $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn ../$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c @touch $@ # Create link to sub arch includes $(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) - @echo ' Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink' + @echo ' SYMLINK include/asm-$(ARCH)/arch -> include/asm-$(ARCH)/$(SARCH)' @rm -f include/asm-$(ARCH)/arch - @ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch + @ln -sf $(SARCH) $(srctree)/include/asm-$(ARCH)/arch @touch $@ + +archclean: + $(Q)if [ -e arch/$(ARCH)/boot ]; then \ + $(MAKE) $(clean)=arch/$(ARCH)/boot; \ + fi + +CLEAN_FILES += \ + $(MACHINE)/boot/zImage \ + $(SRC_ARCH)/.links \ + $(srctree)/include/asm-$(ARCH)/.arch + +MRPROPER_FILES += \ + $(SRC_ARCH)/drivers \ + $(SRC_ARCH)/boot \ + $(SRC_ARCH)/lib \ + $(SRC_ARCH)/arch \ + $(SRC_ARCH)/kernel/vmlinux.lds.S \ + $(SRC_ARCH)/kernel/asm-offsets.c + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' + echo '* Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' +endef diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/README.mm linux-2.6.19.2.dev/arch/cris/arch-v10/README.mm --- linux-2.6.19.2.old/arch/cris/arch-v10/README.mm 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/README.mm 2005-08-23 11:44:32.000000000 +0200 @@ -3,6 +3,9 @@ HISTORY: $Log: README.mm,v $ +Revision 1.2 2005/08/23 09:44:32 starvik +extern inline -> static inline. Patch provided by Adrian Bunk + Revision 1.1 2001/12/17 13:59:27 bjornw Initial revision diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/Makefile 2006-11-29 17:05:40.000000000 +0100 @@ -1,13 +1,21 @@ # -# arch/cris/boot/Makefile +# arch/cris/arch-v10/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss -compressed/vmlinuz: - @$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(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 $< $@ + @echo ' Kernel: $@ is ready' diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/Makefile 2006-10-11 17:47:04.000000000 +0200 @@ -1,45 +1,34 @@ # -# create a compressed vmlinuz image from the binary vmlinux.bin file +# arch/cris/arch-v10/boot/compressed/Makefile # -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -melf $(LINUXINCLUDE) CFLAGS = -O2 LD = ld-cris +LDFLAGS = -T $(obj)/decompress.ld +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o -# files to compress -SYSTEM = $(objtree)/vmlinux.bin +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -all: $(target_compressed_dir)/vmlinuz +targets := vmlinux piggy.gz decompress.o decompress.bin -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -# Create vmlinuz image in top-level build directory -$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - @echo " COMPR vmlinux.bin --> vmlinuz" - @cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz - @rm -f piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ +$(obj)/head.o: $(obj)/head.S .config + @$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ -$(target)/misc.o: $(src)/misc.c - $(CC) -D__KERNEL__ -c $< -o $@ +$(obj)/misc.o: $(obj)/misc.c .config + @$(CC) -D__KERNEL__ -c $< -o $@ -# gzip the kernel image - -piggy.img: $(SYSTEM) - @cat $(SYSTEM) | gzip -f -9 > piggy.img - -$(target): - mkdir -p $(target) - -clean: - rm -f piggy.img $(objtree)/vmlinuz +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/misc.c linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/misc.c --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/misc.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/misc.c 2006-10-13 14:43:10.000000000 +0200 @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.6 2003/10/27 08:04:31 starvik Exp $ + * $Id: misc.c,v 1.7 2006/10/13 12:43:10 starvik Exp $ * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/Makefile 2006-11-30 11:42:39.000000000 +0100 @@ -1,56 +1,38 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux $(LINUXINCLUDE) CFLAGS = -O2 -LD = gcc-cris -mlinux -nostdlib +AFLAGS = -traditional +LD = gcc-cris -mlinux -nostdlib +LDFLAGS = -T $(obj)/rescue.ld OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-y = head.o +OBJECT = $(obj)/$(obj-y) -all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin +targets := rescue.o rescue.bin -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin -# Place a copy in top-level build directory - cp -p $(target)/rescue.bin $(objtree) +$(obj)/rescue.o: $(OBJECT) FORCE + $(call if_changed,ld) -$(target)/testrescue.bin: $(target) $(target)/testrescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) + +$(obj)/testrescue.bin: $(obj)/testrescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin # Pad it to 784 bytes dd if=/dev/zero of=tmp2423 bs=1 count=784 cat tr.bin tmp2423 >testrescue_tmp.bin - dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784 + dd if=testrescue_tmp.bin of=$(obj)/testrescue.bin bs=1 count=784 rm tr.bin tmp2423 testrescue_tmp.bin -$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin +$(obj)/kimagerescue.bin: $(obj)/kimagerescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/kimagerescue.o ktr.bin # Pad it to 784 bytes, that's what the rescue loader expects dd if=/dev/zero of=tmp2423 bs=1 count=784 cat ktr.bin tmp2423 >kimagerescue_tmp.bin - dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784 + dd if=kimagerescue_tmp.bin of=$(obj)/kimagerescue.bin bs=1 count=784 rm ktr.bin tmp2423 kimagerescue_tmp.bin - -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/testrescue.o: $(src)/testrescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/kimagerescue.o: $(src)/kimagerescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/head.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/head.S --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/head.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/head.S 2006-10-13 14:43:10.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $ +/* $Id: head.S,v 1.9 2006/10/13 12:43:10 starvik Exp $ * * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/kimagerescue.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/kimagerescue.S --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/kimagerescue.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/kimagerescue.S 2006-11-29 17:05:41.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: kimagerescue.S,v 1.3 2006/11/29 16:05:41 ricardw Exp $ * * Rescue code to be prepended on a kimage and copied to the * rescue serial port. @@ -7,7 +7,7 @@ */ #define ASSEMBLER_MACROS_ONLY -#include +#include #define CODE_START 0x40004000 #define CODE_LENGTH 784 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/testrescue.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/testrescue.S --- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/testrescue.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/testrescue.S 2006-11-29 17:05:41.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: testrescue.S,v 1.2 2006/11/29 16:05:41 ricardw Exp $ * * Simple testcode to download by the rescue block. * Just lits some LEDs to show it was downloaded correctly. @@ -7,7 +7,7 @@ */ #define ASSEMBLER_MACROS_ONLY -#include +#include .text diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/defconfig linux-2.6.19.2.dev/arch/cris/arch-v10/defconfig --- linux-2.6.19.2.old/arch/cris/arch-v10/defconfig 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/defconfig 2006-01-03 15:48:23.000000000 +0100 @@ -106,7 +106,6 @@ CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y # CONFIG_ETRAX_I2C_EEPROM is not set CONFIG_ETRAX_GPIO=y -CONFIG_ETRAX_PA_BUTTON_BITMASK=02 CONFIG_ETRAX_PA_CHANGEABLE_DIR=00 CONFIG_ETRAX_PA_CHANGEABLE_BITS=FF CONFIG_ETRAX_PB_CHANGEABLE_DIR=00 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Kconfig --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Kconfig 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Kconfig 2007-01-09 10:29:18.000000000 +0100 @@ -6,6 +6,12 @@ This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. +config ETRAX_NO_PHY + bool "PHY not present" + depends on ETRAX_ETHERNET + help + Enable if PHY is not present. + choice prompt "Network LED behavior" depends on ETRAX_ETHERNET @@ -90,7 +96,7 @@ config ETRAX_SERIAL_PORT0_DMA6_OUT bool "DMA 6" - + depends on !ETRAX_DEBUG_PORT0 endchoice choice @@ -103,7 +109,7 @@ config ETRAX_SERIAL_PORT0_DMA7_IN bool "DMA 7" - + depends on !ETRAX_DEBUG_PORT0 endchoice choice @@ -204,7 +210,7 @@ config ETRAX_SERIAL_PORT1_DMA8_OUT bool "DMA 8" - + depends on !ETRAX_DEBUG_PORT1 endchoice choice @@ -217,7 +223,7 @@ config ETRAX_SERIAL_PORT1_DMA9_IN bool "DMA 9" - + depends on !ETRAX_DEBUG_PORT1 endchoice choice @@ -321,7 +327,7 @@ config ETRAX_SERIAL_PORT2_DMA2_OUT bool "DMA 2" - + depends on !ETRAX_DEBUG_PORT2 endchoice choice @@ -334,7 +340,7 @@ config ETRAX_SERIAL_PORT2_DMA3_IN bool "DMA 3" - + depends on !ETRAX_DEBUG_PORT2 endchoice choice @@ -435,7 +441,7 @@ config ETRAX_SERIAL_PORT3_DMA4_OUT bool "DMA 4" - + depends on !ETRAX_DEBUG_PORT3 endchoice choice @@ -448,7 +454,7 @@ config ETRAX_SERIAL_PORT3_DMA5_IN bool "DMA 5" - + depends on !ETRAX_DEBUG_PORT3 endchoice choice @@ -514,8 +520,7 @@ bool "RS-485 support" depends on ETRAX_SERIAL help - Enables support for RS-485 serial communication. For a primer on - RS-485, see . + Enables support for RS-485 serial communication. config ETRAX_RS485_ON_PA bool "RS-485 mode on PA" @@ -541,6 +546,27 @@ loopback. Not all products are able to do this in software only. Axis 2400/2401 must disable receiver. +config ETRAX_SYNCHRONOUS_SERIAL + bool "Synchronous serial port driver" + help + Select this to enable the synchronous serial port driver. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT0 + bool "Synchronous serial port 0 enabled (sser1)" + depends on ETRAX_SYNCHRONOUS_SERIAL + +config ETRAX_SYNCHRONOUS_SERIAL0_DMA + bool "Use DMA for synchronous serial port 0" + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0 + +config ETRAX_SYNCHRONOUS_SERIAL_PORT1 + bool "Synchronous serial port 1 enabled (sser3)" + depends on ETRAX_SYNCHRONOUS_SERIAL + +config ETRAX_SYNCHRONOUS_SERIAL1_DMA + bool "Use DMA for synchronous serial port 1" + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1 + config ETRAX_IDE bool "ATA/IDE support" select IDE @@ -604,8 +630,7 @@ select MTD select MTD_CFI select MTD_CFI_AMDSTD - select MTD_OBSOLETE_CHIPS - select MTD_AMDSTD + select MTD_JEDECPROBE select MTD_CHAR select MTD_BLOCK select MTD_PARTITIONS @@ -615,6 +640,15 @@ This option enables MTD mapping of flash devices. Needed to use flash memories. If unsure, say Y. +config ETRAX_AXISFLASHMAP_MTD0WHOLE + bool "MTD0 is whole boot flash device" + depends on ETRAX_AXISFLASHMAP + default N + help + When this option is not set, mtd0 refers to the first partition + on the boot flash device. When set, mtd0 refers to the whole + device, with mtd1 referring to the first partition etc. + config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -715,19 +749,6 @@ Remember that you need to setup the port directions appropriately in the General configuration. -config ETRAX_PA_BUTTON_BITMASK - hex "PA-buttons bitmask" - depends on ETRAX_GPIO - default "02" - help - This is a bitmask with information about what bits on PA that - are used for buttons. - Most products has a so called TEST button on PA1, if that's true - use 02 here. - Use 00 if there are no buttons on PA. - If the bitmask is <> 00 a button driver will be included in the gpio - driver. ETRAX general I/O support must be enabled. - config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" depends on ETRAX_GPIO @@ -768,6 +789,40 @@ Bit set = changeable. You probably want 00 here. +config ETRAX_DEF_R_PORT_G_DIR + bool "Port G Output" + help + CONFIG_ETRAX_DEF_R_PORT_G_DIR: + Set the direction of specified pins to output. + +config ETRAX_DEF_R_PORT_G0_DIR_OUT + bool "G0" + depends on ETRAX_DEF_R_PORT_G_DIR + help + CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT: + Set G0 to output. + +config ETRAX_DEF_R_PORT_G8_15_DIR_OUT + bool "G8-G15" + depends on ETRAX_DEF_R_PORT_G_DIR + help + CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT: + Set G8-G15 to output. + +config ETRAX_DEF_R_PORT_G16_23_DIR_OUT + bool "G16-G23" + depends on ETRAX_DEF_R_PORT_G_DIR + help + CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT: + Set G16-G23 to output. + +config ETRAX_DEF_R_PORT_G24_DIR_OUT + bool "G24" + depends on ETRAX_DEF_R_PORT_G_DIR + help + CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT: + Set G24 to output. + config ETRAX_RTC bool "Real Time Clock support" depends on ETRAX_ARCH_V10 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Makefile 2005-12-12 10:05:46.000000000 +0100 @@ -8,5 +8,5 @@ obj-$(CONFIG_ETRAX_GPIO) += gpio.o obj-$(CONFIG_ETRAX_DS1302) += ds1302.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o - +obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/axisflashmap.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/axisflashmap.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/axisflashmap.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/axisflashmap.c 2006-11-22 13:26:55.000000000 +0100 @@ -11,6 +11,26 @@ * partition split defined below. * * $Log: axisflashmap.c,v $ + * Revision 1.17 2006/11/22 12:26:55 ricardw + * Added CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE option which when enabled puts mtd0 + * as whole device, with first partition at mtd1, etc. + * + * Revision 1.16 2006/10/30 15:17:57 pkj + * Avoid a compiler warning. + * + * Revision 1.15 2006/10/13 12:43:10 starvik + * Merge of 2.6.18 + * + * Revision 1.14 2006/08/30 13:20:00 karljope + * Do not use deprecated amd_flash to probe flash memory. + * Probe for flash chip with CFI first and if no chip was found try jedec_probe. + * + * Revision 1.13 2006/01/04 06:09:45 starvik + * Merge of Linux 2.6.15 + * + * Revision 1.12 2005/06/21 09:13:06 starvik + * Change const char* to const char[] to save space (from domen@coderock.org). + * * Revision 1.11 2004/11/15 10:27:14 starvik * Corrected typo (Thanks to Milton Miller ). * @@ -300,6 +320,15 @@ }, }; +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -312,12 +341,12 @@ "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", map_cs->name, map_cs->size, map_cs->map_priv_1); -#ifdef CONFIG_MTD_AMDSTD - mtd_cs = do_map_probe("amd_flash", map_cs); -#endif #ifdef CONFIG_MTD_CFI + mtd_cs = do_map_probe("cfi_probe", map_cs); +#endif +#ifdef CONFIG_MTD_JEDECPROBE if (!mtd_cs) { - mtd_cs = do_map_probe("cfi_probe", map_cs); + mtd_cs = do_map_probe("jedec_probe", map_cs); } #endif @@ -396,7 +425,7 @@ struct partitiontable_head *ptable_head = NULL; 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"; + const char pmsg[] = " /dev/flash%d at 0x%08x, size 0x%08x\n"; if (!(mymtd = flash_probe())) { /* There's no reason to use this module if no flash chip can @@ -491,6 +520,16 @@ pidx++; } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (mymtd) { + main_partition.size = mymtd->size; + err = add_mtd_partitions(mymtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); + } +#endif + if (mymtd) { if (use_default_ptable) { printk(KERN_INFO " Using default partition table.\n"); @@ -524,7 +563,7 @@ } printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); + printk(pmsg, pidx, (unsigned)romfs_start, (unsigned)romfs_length); err = mtdram_init_device(mtd_ram, (void*)romfs_start, romfs_length, "romfs"); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/ds1302.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/ds1302.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/ds1302.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/ds1302.c 2006-10-27 16:31:23.000000000 +0200 @@ -6,136 +6,9 @@ *! *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init *! -*! $Log: ds1302.c,v $ -*! Revision 1.18 2005/01/24 09:11:26 mikaelam -*! Minor changes to get DS1302 RTC chip driver to work -*! -*! Revision 1.17 2005/01/05 06:11:22 starvik -*! No need to do local_irq_disable after local_irq_save. -*! -*! Revision 1.16 2004/12/13 12:21:52 starvik -*! Added I/O and DMA allocators from Linux 2.4 -*! -*! Revision 1.14 2004/08/24 06:48:43 starvik -*! Whitespace cleanup -*! -*! Revision 1.13 2004/05/28 09:26:59 starvik -*! Modified I2C initialization to work in 2.6. -*! -*! Revision 1.12 2004/05/14 07:58:03 starvik -*! Merge of changes from 2.4 -*! -*! Revision 1.10 2004/02/04 09:25:12 starvik -*! Merge of Linux 2.6.2 -*! -*! Revision 1.9 2003/07/04 08:27:37 starvik -*! Merge of Linux 2.5.74 -*! -*! Revision 1.8 2003/04/09 05:20:47 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.6 2003/01/09 14:42:51 starvik -*! Merge of Linux 2.5.55 -*! -*! Revision 1.4 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.3 2002/11/20 11:56:10 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.2 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.15 2002/10/11 16:14:33 johana -*! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the -*! trcklecharge register. -*! -*! Revision 1.14 2002/10/10 12:15:38 magnusmn -*! Added support for having the RST signal on bit g0 -*! -*! Revision 1.13 2002/05/29 15:16:08 johana -*! Removed unused variables. -*! -*! Revision 1.12 2002/04/10 15:35:25 johana -*! Moved probe function closer to init function and marked it __init. -*! -*! Revision 1.11 2001/06/14 12:35:52 jonashg -*! The ATA hack is back. It is unfortunately the only way to set g27 to output. -*! -*! Revision 1.9 2001/06/14 10:00:14 jonashg -*! No need for tempudelay to be inline anymore (had to adjust the usec to -*! loops conversion because of this to make it slow enough to be a udelay). -*! -*! Revision 1.8 2001/06/14 08:06:32 jonashg -*! Made tempudelay delay usecs (well, just a tad more). -*! -*! Revision 1.7 2001/06/13 14:18:11 jonashg -*! Only allow processes with SYS_TIME capability to set time and charge. -*! -*! Revision 1.6 2001/06/12 15:22:07 jonashg -*! * Made init function __init. -*! * Parameter to out_byte() is unsigned char. -*! * The magic number 42 has got a name. -*! * Removed comment about /proc (nothing is exported there). -*! -*! Revision 1.5 2001/06/12 14:35:13 jonashg -*! Gave the module a name and added it to printk's. -*! -*! Revision 1.4 2001/05/31 14:53:40 jonashg -*! Made tempudelay() inline so that the watchdog doesn't reset (see -*! function comment). -*! -*! Revision 1.3 2001/03/26 16:03:06 bjornw -*! Needs linux/config.h -*! -*! Revision 1.2 2001/03/20 19:42:00 bjornw -*! Use the ETRAX prefix on the DS1302 options -*! -*! Revision 1.1 2001/03/20 09:13:50 magnusmn -*! Linux 2.4 port -*! -*! Revision 1.10 2000/07/05 15:38:23 bjornw -*! Dont update kernel time when a RTC_SET_TIME is done -*! -*! Revision 1.9 2000/03/02 15:42:59 macce -*! * Hack to make RTC work on all 2100/2400 -*! -*! Revision 1.8 2000/02/23 16:59:18 torbjore -*! added setup of R_GEN_CONFIG when RTC is connected to the generic port. -*! -*! Revision 1.7 2000/01/17 15:51:43 johana -*! Added RTC_SET_CHARGE ioctl to enable trickle charger. -*! -*! Revision 1.6 1999/10/27 13:19:47 bjornw -*! Added update_xtime_from_cmos which reads back the updated RTC into the kernel. -*! /dev/rtc calls it now. -*! -*! Revision 1.5 1999/10/27 12:39:37 bjornw -*! Disabled superuser check. Anyone can now set the time. -*! -*! Revision 1.4 1999/09/02 13:27:46 pkj -*! Added shadow for R_PORT_PB_CONFIG. -*! Renamed port_g_shadow to port_g_data_shadow. -*! -*! Revision 1.3 1999/09/02 08:28:06 pkj -*! Made it possible to select either port PB or the generic port for the RST -*! signal line to the DS1302 RTC. -*! Also make sure the RST bit is configured as output on Port PB (if used). -*! -*! Revision 1.2 1999/09/01 14:47:20 bjornw -*! Added support for /dev/rtc operations with ioctl RD_TIME and SET_TIME to read -*! and set the date. Register as major 121. -*! -*! Revision 1.1 1999/09/01 09:45:29 bjornw -*! Implemented a DS1302 RTC driver. -*! -*! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999, 2000, 2001, 2002, 2003, 2004 Axis Communications AB, LUND, SWEDEN -*! -*! $Id: ds1302.c,v 1.18 2005/01/24 09:11:26 mikaelam Exp $ +*! (C) Copyright 1999-2006 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ @@ -305,14 +178,7 @@ void ds1302_writereg(int reg, unsigned char val) { -#ifndef CONFIG_ETRAX_RTC_READONLY int do_writereg = 1; -#else - int do_writereg = 0; - - if (reg == RTC_TRICKLECHARGER) - do_writereg = 1; -#endif if (do_writereg) { ds1302_wenable(); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/eeprom.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/eeprom.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/eeprom.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/eeprom.c 2006-10-13 14:43:10.000000000 +0200 @@ -20,6 +20,9 @@ *! in the spin-lock. *! *! $Log: eeprom.c,v $ +*! Revision 1.13 2006/10/13 12:43:10 starvik +*! Merge of 2.6.18 +*! *! Revision 1.12 2005/06/19 17:06:46 starvik *! Merge of Linux 2.6.12. *! diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/gpio.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/gpio.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/gpio.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/gpio.c 2007-02-05 12:54:34.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ +/* $Id: gpio.c,v 1.28 2007/02/05 11:54:34 pkj Exp $ * * Etrax general port I/O device * @@ -9,6 +9,40 @@ * Johan Adolfsson (read/set directions, write, port G) * * $Log: gpio.c,v $ + * Revision 1.28 2007/02/05 11:54:34 pkj + * Merge of Linux 2.6.19 + * + * Revision 1.27 2006/12/12 11:08:30 edgar + * In etrax_gpio_wake_up_check(), make flags unsigned long. + * + * Revision 1.26 2006/11/02 10:54:29 pkj + * Restored unique device id for request_irq() which was lost in the + * merge of 2.6.18. + * + * Revision 1.25 2006/10/13 12:43:10 starvik + * Merge of 2.6.18 + * + * Revision 1.24 2006/07/13 07:42:20 starvik + * Set unique device id in request_irq + * + * Revision 1.23 2006/06/21 09:38:46 starvik + * Use correct spinlock macros + * + * Revision 1.22 2005/08/29 07:32:16 starvik + * Merge of 2.6.13 + * + * Revision 1.21 2005/08/16 17:10:54 edgar + * dont leave locked spinlocks when returning. + * + * Revision 1.20 2005/08/15 13:10:47 orjanf + * Don't link struct into alarmlist until fully initialized. + * + * Revision 1.19 2005/07/13 11:43:11 karljope + * Corrected typo + * + * Revision 1.18 2005/06/21 12:26:53 starvik + * Improved alarm list locking. + * * Revision 1.17 2005/06/19 17:06:46 starvik * Merge of Linux 2.6.12. * @@ -277,7 +311,7 @@ unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned long data; - spin_lock(&gpio_lock); + spin_lock_irq(&gpio_lock); poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { unsigned long flags; @@ -297,15 +331,17 @@ data = *R_PORT_PB_DATA; else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; - else + else { + spin_unlock_irq(&gpio_lock); return 0; + } if ((data & priv->highalarm) || (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } - spin_unlock(&gpio_lock); + spin_unlock_irq(&gpio_lock); DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); @@ -314,10 +350,12 @@ int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; int ret = 0; - spin_lock(&gpio_lock); + unsigned long flags; + spin_lock_irqsave(&gpio_lock, flags); + priv = alarmlist; while (priv) { if (USE_PORTS(priv)) { data = *priv->port; @@ -332,12 +370,12 @@ } priv = priv->next; } - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { if (gpio_some_alarms) { etrax_gpio_wake_up_check(); @@ -347,7 +385,7 @@ } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_pa_interrupt(int irq, void *dev_id) { unsigned long tmp; spin_lock(&gpio_lock); @@ -376,9 +414,6 @@ struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned char data, clk_mask, data_mask, write_msb; unsigned long flags; - - spin_lock(&gpio_lock); - ssize_t retval = count; if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { return -EFAULT; @@ -394,6 +429,7 @@ if (clk_mask == 0 || data_mask == 0) { return -EPERM; } + spin_lock_irq(&gpio_lock); write_msb = priv->write_msb; D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); while (count--) { @@ -425,7 +461,7 @@ } } } - spin_unlock(&gpio_lock); + spin_unlock_irq(&gpio_lock); return retval; } @@ -445,13 +481,12 @@ if (!priv) return -ENOMEM; + memset(priv, 0, sizeof(*priv)); priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; if (USE_PORTS(priv)) { /* A and B */ priv->port = ports[p]; priv->shadow = shads[p]; @@ -476,6 +511,12 @@ filp->private_data = (void *)priv; + /* link it into our alarmlist */ + spin_lock_irq(&gpio_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&gpio_lock); + return 0; } @@ -485,10 +526,10 @@ struct gpio_private *p; struct gpio_private *todel; - spin_lock(&gpio_lock); + spin_lock_irq(&gpio_lock); - p = alarmlist; - todel = (struct gpio_private *)filp->private_data; + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; /* unlink from alarmlist and free the private structure */ @@ -506,12 +547,13 @@ while (p) { if (p->highalarm | p->lowalarm) { gpio_some_alarms = 1; + spin_unlock_irq(&gpio_lock); return 0; } p = p->next; } gpio_some_alarms = 0; - spin_unlock(&gpio_lock); + spin_unlock_irq(&gpio_lock); return 0; } @@ -691,6 +733,8 @@ /* Must update gpio_some_alarms */ struct gpio_private *p = alarmlist; int some_alarms; + spin_lock_irq(&gpio_lock); + p = alarmlist; some_alarms = 0; while (p) { if (p->highalarm | p->lowalarm) { @@ -700,6 +744,7 @@ p = p->next; } gpio_some_alarms = some_alarms; + spin_unlock_irq(&gpio_lock); } break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ @@ -937,11 +982,11 @@ * in some tests. */ if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { + IRQF_SHARED | IRQF_DISABLED,"gpio poll", gpio_name)) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); } if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { + IRQF_SHARED | IRQF_DISABLED,"gpio PA", gpio_name)) { printk(KERN_CRIT "err: PA irq for gpio\n"); } diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/i2c.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/i2c.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/i2c.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/i2c.c 2006-10-13 14:43:10.000000000 +0200 @@ -11,7 +11,25 @@ *! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - *! don't use PB_I2C if DS1302 uses same bits, *! use PB. +*! June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now +*! generates nack on last received byte, +*! instead of ack. +*! i2c_getack changed data level while clock +*! was high, causing DS75 to see a stop condition +*! *! $Log: i2c.c,v $ +*! Revision 1.17 2006/10/13 12:43:10 starvik +*! Merge of 2.6.18 +*! +*! Revision 1.16 2005/09/29 13:33:35 bjarne +*! If "first" should have any purpos it should probably change value.... +*! +*! Revision 1.15 2005/08/29 07:32:16 starvik +*! Merge of 2.6.13 +*! +*! Revision 1.14 2005/06/30 18:07:31 starvik +*! Added the sendnack patch from 2.4. +*! *! Revision 1.13 2005/03/07 13:13:07 starvik *! Added spinlocks to protect states etc *! @@ -84,7 +102,7 @@ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ +/* $Id: i2c.c,v 1.17 2006/10/13 12:43:10 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -480,7 +498,7 @@ i2c_delay(CLOCK_HIGH_TIME); i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_LOW_TIME); - + i2c_dir_in(); } @@ -622,7 +640,7 @@ * last received byte needs to be nacked * instead of acked */ - i2c_sendack(); + i2c_sendnack(); /* * end sequence */ @@ -708,6 +726,7 @@ if (!first) { return res; } + first = 0; /* Setup and enable the Port B I2C interface */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/pcf8563.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/pcf8563.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/pcf8563.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/pcf8563.c 2006-10-27 17:22:12.000000000 +0200 @@ -8,14 +8,13 @@ * low detector are also provided. All address and data are transferred * serially via two-line bidirectional I2C-bus. Maximum bus speed is * 400 kbits/s. The built-in word address register is incremented - * automatically after each written or read bute. + * automatically after each written or read byte. * - * Copyright (c) 2002, Axis Communications AB + * Copyright (c) 2002-2006, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg . * - * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ */ #include @@ -27,93 +26,105 @@ #include #include #include -#include #include #include #include -#include #include + #include "i2c.h" -#define PCF8563_MAJOR 121 /* Local major number. */ -#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ -#define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.11 $" - -/* I2C bus slave registers. */ -#define RTC_I2C_READ 0xa3 -#define RTC_I2C_WRITE 0xa2 +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_NAME "PCF8563" +#define DRIVER_VERSION "$Revision: 1.18 $" /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ - + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low = 0; + static struct file_operations pcf8563_fops = { - .owner = THIS_MODULE, - .ioctl = pcf8563_ioctl, + owner: THIS_MODULE, + ioctl: pcf8563_ioctl, }; unsigned char -pcf8563_readreg(int reg) +pcf8563_readreg(int reg) { - unsigned char res = i2c_readreg(RTC_I2C_READ, reg); + unsigned char res = rtc_read(reg); - /* The PCF8563 does not return 0 for unimplemented bits */ - switch(reg) - { + /* The PCF8563 does not return 0 for unimplemented bits. */ + switch (reg) { case RTC_SECONDS: case RTC_MINUTES: - res &= 0x7f; - break; + res &= 0x7F; + break; case RTC_HOURS: case RTC_DAY_OF_MONTH: - res &= 0x3f; - break; + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; case RTC_MONTH: - res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ - break; + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; } return res; } void -pcf8563_writereg(int reg, unsigned char val) +pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } void get_rtc_time(struct rtc_time *tm) { - tm->tm_sec = rtc_read(RTC_SECONDS); - tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); tm->tm_hour = rtc_read(RTC_HOURS); tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); - tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + if (tm->tm_sec & 0x80) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7f; - tm->tm_min &= 0x7f; - tm->tm_hour &= 0x3f; - tm->tm_mday &= 0x3f; - tm->tm_mon &= 0x1f; + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); @@ -126,17 +137,25 @@ int __init pcf8563_init(void) { - int ret; + static int res = 0; + static int first = 1; + + if (!first) { + return res; + } + first = 0; - if ((ret = i2c_init())) { - printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); - return ret; + /* Initiate the i2c protocol. */ + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; } /* * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq, clear the - * Voltage Low bit, and resetting all alarms. + * clearing control1, control2 and clk freq and resetting + * all alarms. */ if (rtc_write(RTC_CONTROL1, 0x00) < 0) goto err; @@ -147,41 +166,44 @@ if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) goto err; - /* Clear the VL bit in the seconds register. */ - ret = rtc_read(RTC_SECONDS); - - if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) goto err; - + /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); - - return 0; + + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit pcf8563_exit(void) { if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { - printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); + printk(KERN_INFO "%s: Unable to unregister device.\n", + PCF8563_NAME); } } @@ -190,7 +212,8 @@ * POSIX says so! */ int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -201,123 +224,151 @@ switch (cmd) { case RTC_RD_TIME: - { - struct rtc_time tm; - - spin_lock(&rtc_lock); - get_rtc_time(&tm); + { + struct rtc_time tm; - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { - spin_unlock(&rtc_lock); - return -EFAULT; - } + spin_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { spin_unlock(&rtc_lock); - return 0; + return -EFAULT; } - break; + + spin_unlock(&rtc_lock); + + return 0; + } case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) return -EPERM; -#else - int leap; - int century; - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0; - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - spin_lock(&rtc_lock); - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - spin_unlock(&rtc_lock); + if (copy_from_user(&tm, (struct rtc_time *) arg, + sizeof tm)) { + return -EFAULT; + } - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) { + return -EINVAL; } - case RTC_VLOW_RD: - { - int vl_bit = 0; + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + spin_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + spin_unlock(&rtc_lock); return 0; } + case RTC_VLOW_RD: + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); + } + + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) { + return -EFAULT; + } + + return 0; case RTC_VLOW_SET: { - /* Clear the VL bit in the seconds register */ + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ + int ret = rtc_read(RTC_SECONDS); rtc_write(RTC_SECONDS, (ret & 0x7F)); + /* Clear the cached value. */ + voltage_low = 0; + return 0; } - default: - return -ENOTTY; + return -ENOTTY; } return 0; } -static int __init +static int __init pcf8563_register(void) { - pcf8563_init(); + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); + printk(KERN_INFO "%s: Unable to get major numer %d for RTC " + "device.\n", PCF8563_NAME, PCF8563_MAJOR); return -1; } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - return 0; + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } + + return 0; } module_init(pcf8563_register); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/sync_serial.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/sync_serial.c --- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/sync_serial.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/sync_serial.c 2007-02-05 12:56:34.000000000 +0100 @@ -0,0 +1,1329 @@ +/* + * Simple synchronous serial port driver for ETRAX 100LX. + * + * Synchronous serial ports are used for continuous streamed data like audio. + * The default setting for this driver is compatible with the STA 013 MP3 + * decoder. The driver can easily be tuned to fit other audio encoder/decoders + * and SPI + * + * Copyright (c) 2001-2006 Axis Communications AB + * + * Author: Mikael Starvik, Johan Adolfsson + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ +/* */ +/* Three DMA descriptors are linked together. Each DMA descriptor is */ +/* responsible for port->bufchunk of a common buffer. */ +/* */ +/* +---------------------------------------------+ */ +/* | +----------+ +----------+ +----------+ | */ +/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ */ +/* +----------+ +----------+ +----------+ */ +/* | | | */ +/* v v v */ +/* +-------------------------------------+ */ +/* | BUFFER | */ +/* +-------------------------------------+ */ +/* |<- data_avail ->| */ +/* readp writep */ +/* */ +/* If the application keeps up the pace readp will be right after writep.*/ +/* If the application can't keep the pace we have to throw away data. */ +/* The idea is that readp should be ready with the data pointed out by */ +/* Descr[i] when the DMA has filled in Descr[i+1]. */ +/* Otherwise we will discard */ +/* the rest of the data pointed out by Descr1 and set readp to the start */ +/* of Descr2 */ + +#define SYNC_SERIAL_MAJOR 125 + +/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ +/* words can be handled */ +#define IN_BUFFER_SIZE 12288 +#define IN_DESCR_SIZE 256 +#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) +#define OUT_BUFFER_SIZE 4096 + +#define DEFAULT_FRAME_RATE 0 +#define DEFAULT_WORD_RATE 7 + +/* NOTE: Enabling some debug will likely cause overrun or underrun, + * especially if manual mode is use. + */ +#define DEBUG(x) +#define DEBUGREAD(x) +#define DEBUGWRITE(x) +#define DEBUGPOLL(x) +#define DEBUGRXINT(x) +#define DEBUGTXINT(x) + +/* Define some macros to access ETRAX 100 registers */ +#define SETF(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_FIELD_(reg##_, field##_, val) +#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val) + +typedef struct sync_port +{ + /* Etrax registers and bits*/ + const volatile unsigned * const status; + volatile unsigned * const ctrl_data; + volatile unsigned * const output_dma_first; + volatile unsigned char * const output_dma_cmd; + volatile unsigned char * const output_dma_clr_irq; + volatile unsigned * const input_dma_first; + volatile unsigned char * const input_dma_cmd; + volatile unsigned * const input_dma_descr; + /* 8*4 */ + volatile unsigned char * const input_dma_clr_irq; + volatile unsigned * const data_out; + const volatile unsigned * const data_in; + char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */ + + char output_dma_bit; /* In R_IRQ_MASK2_RD */ + /* End of fields initialised in array */ + char started; /* 1 if port has been started */ + char port_nbr; /* Port 0 or 1 */ + char busy; /* 1 if port is busy */ + + char enabled; /* 1 if port is enabled */ + char use_dma; /* 1 if port uses dma */ + char tr_running; + + char init_irqs; + + unsigned int ctrl_data_shadow; /* Register shadow */ + volatile unsigned int out_count; /* Remaining bytes for current transfer */ + unsigned char* outp; /* Current position in out_buffer */ + /* 16*4 */ + volatile unsigned char* volatile readp; /* Next byte to be read by application */ + volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ + unsigned int in_buffer_size; + unsigned int inbufchunk; + struct etrax_dma_descr out_descr __attribute__ ((aligned(32))); + struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32))); + unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); + unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); + unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); + struct etrax_dma_descr* next_rx_desc; + struct etrax_dma_descr* prev_rx_desc; + int full; + + wait_queue_head_t out_wait_q; + wait_queue_head_t in_wait_q; +} sync_port; + + +static int etrax_sync_serial_init(void); +static void initialize_port(int portnbr); +static inline int sync_data_avail(struct sync_port *port); + +static int sync_serial_open(struct inode *, struct file*); +static int sync_serial_release(struct inode*, struct file*); +static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); + +static int sync_serial_ioctl(struct inode*, struct file*, + unsigned int cmd, unsigned long arg); +static ssize_t sync_serial_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)) +#define SYNC_SER_DMA +#endif + +static void send_word(sync_port* port); +static void start_dma(struct sync_port *port, const char* data, int count); +static void start_dma_in(sync_port* port); +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); +#endif +#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)) +#define SYNC_SER_MANUAL +#endif +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +/* The ports */ +static struct sync_port ports[]= +{ + { + .status = R_SYNC_SERIAL1_STATUS, + .ctrl_data = R_SYNC_SERIAL1_CTRL, + .output_dma_first = R_DMA_CH8_FIRST, + .output_dma_cmd = R_DMA_CH8_CMD, + .output_dma_clr_irq = R_DMA_CH8_CLR_INTR, + .input_dma_first = R_DMA_CH9_FIRST, + .input_dma_cmd = R_DMA_CH9_CMD, + .input_dma_descr = R_DMA_CH9_DESCR, + .input_dma_clr_irq = R_DMA_CH9_CLR_INTR, + .data_out = R_SYNC_SERIAL1_TR_DATA, + .data_in = R_SYNC_SERIAL1_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + }, + { + .status = R_SYNC_SERIAL3_STATUS, + .ctrl_data = R_SYNC_SERIAL3_CTRL, + .output_dma_first = R_DMA_CH4_FIRST, + .output_dma_cmd = R_DMA_CH4_CMD, + .output_dma_clr_irq = R_DMA_CH4_CLR_INTR, + .input_dma_first = R_DMA_CH5_FIRST, + .input_dma_cmd = R_DMA_CH5_CMD, + .input_dma_descr = R_DMA_CH5_DESCR, + .input_dma_clr_irq = R_DMA_CH5_CLR_INTR, + .data_out = R_SYNC_SERIAL3_TR_DATA, + .data_in = R_SYNC_SERIAL3_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + } +}; + +/* Register shadows */ +static unsigned sync_serial_prescale_shadow = 0; + +#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port)) + +static struct file_operations sync_serial_fops = { + .owner = THIS_MODULE, + .write = sync_serial_write, + .read = sync_serial_read, + .poll = sync_serial_poll, + .ioctl = sync_serial_ioctl, + .open = sync_serial_open, + .release = sync_serial_release +}; + +static int __init etrax_sync_serial_init(void) +{ + ports[0].enabled = 0; + ports[1].enabled = 0; + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) { + printk(KERN_CRIT "ETRAX100LX sync_serial: Could not allocate IO group for port %d\n", 0); + return -EBUSY; + } +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk(KERN_CRIT "ETRAX100LX sync_serial: Could not allocate IO group for port %d\n", 1); + return -EBUSY; + } +#endif + + if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) + { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + cris_free_io_interface(if_sync_serial_3); +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk("unable to get major for synchronous serial port\n"); + return -EBUSY; + } + + /* Deselect synchronous serial ports while configuring. */ + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + /* Initialize Ports */ +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + ports[0].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + ports[0].use_dma = 1; +#else + ports[0].use_dma = 0; +#endif + initialize_port(0); +#endif + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + ports[1].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + ports[1].use_dma = 1; +#else + ports[1].use_dma = 0; +#endif + initialize_port(1); +#endif + + *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */ + + /* Set up timing */ + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = ( + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, DEFAULT_FRAME_RATE) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal)); + + /* Select synchronous ports */ + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + printk("ETRAX 100LX synchronous serial port driver\n"); + return 0; +} + +static void __init initialize_port(int portnbr) +{ + struct sync_port* port = &ports[portnbr]; + + DEBUG(printk("Init sync serial port %d\n", portnbr)); + + port->started = 0; + port->port_nbr = portnbr; + port->busy = 0; + port->tr_running = 0; + + port->out_count = 0; + port->outp = port->out_buffer; + + port->readp = port->flip; + port->writep = port->flip; + port->in_buffer_size = IN_BUFFER_SIZE; + port->inbufchunk = IN_DESCR_SIZE; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc->ctrl = d_eol; + + init_waitqueue_head(&port->out_wait_q); + init_waitqueue_head(&port->in_wait_q); + + port->ctrl_data_shadow = + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) | + IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | + IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) | + IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) | + IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) | + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)| + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high); + + if (port->use_dma) + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, on); + else + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, off); + + *port->ctrl_data = port->ctrl_data_shadow; +} + +static inline int sync_data_avail(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- - ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->in_buffer_size - (start - end); + return avail; +} + +static inline int sync_data_avail_to_end(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + return avail; +} + + +static int sync_serial_open(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + sync_port* port; + int mode; + + DEBUG(printk("Open sync serial port %d\n", dev)); + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + /* Allow open this device twice (assuming one reader and one writer) */ + if (port->busy == 2) + { + DEBUG(printk("Device is busy.. \n")); + return -EBUSY; + } + if (port->init_irqs) { + if (port->use_dma) { + if (port == &ports[0]){ +#ifdef SYNC_SER_DMA + if(request_irq(24, + tr_interrupt, + 0, + "synchronous serial 1 dma tr", + &ports[0])) { + printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ"); + return -EBUSY; + } else if(request_irq(25, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(24, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ"); + return -EBUSY; + } else if (cris_request_dma(8, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 1 TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(9, + "synchronous serial 1 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + cris_free_dma(8, NULL); + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(8); WAIT_DMA(8); + RESET_DMA(9); WAIT_DMA(9); + *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); + *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma8_eop, set) | + IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); + } + else if (port == &ports[1]){ +#ifdef SYNC_SER_DMA + if (request_irq(20, + tr_interrupt, + 0, + "synchronous serial 3 dma tr", + &ports[1])) { + printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); + return -EBUSY; + } else if (request_irq(21, + rx_interrupt, + 0, + "synchronous serial 3 dma rx", + &ports[1])) { + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); + return -EBUSY; + } else if (cris_request_dma(4, + "synchronous serial 3 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(5, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + cris_free_dma(4, NULL); + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't allocate sync serial port 3 RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(4); WAIT_DMA(4); + RESET_DMA(5); WAIT_DMA(5); + *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); + *R_DMA_CH5_CLR_INTR = IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma4_eop, set) | + IO_STATE(R_IRQ_MASK2_SET, dma5_descr, set); + } + start_dma_in(port); + port->init_irqs = 0; + } else { /* !port->use_dma */ +#ifdef SYNC_SER_MANUAL + if (port == &ports[0]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[0])) { + printk("Can't allocate sync serial manual irq"); + return -EBUSY; + } + } else if (port == &ports[1]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[1])) { + printk(KERN_CRIT "Can't allocate sync serial manual irq"); + return -EBUSY; + } + } + port->init_irqs = 0; +#else + panic("sync_serial: Manual mode not supported.\n"); +#endif /* SYNC_SER_MANUAL */ + } + } /* port->init_irqs */ + + port->busy++; + /* Start port if we use it as input */ + mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow); + if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable); + port->started = 1; + *port->ctrl_data = port->ctrl_data_shadow; + if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + DEBUG(printk("sser%d rec started\n", dev)); + } + return 0; +} + +static int sync_serial_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + sync_port* port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + if (port->busy) + port->busy--; + if (!port->busy) + *R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) | + (1 << port->transmitter_ready_bit)); + + return 0; +} + + + +static unsigned int sync_serial_poll(struct file *file, poll_table *wait) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned int mask = 0; + sync_port* port; + DEBUGPOLL( static unsigned int prev_mask = 0; ); + + port = &ports[dev]; + poll_wait(file, &port->out_wait_q, wait); + poll_wait(file, &port->in_wait_q, wait); + /* Some room to write */ + if (port->out_count < OUT_BUFFER_SIZE) + mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ + if (sync_data_avail(port) >= port->inbufchunk) + mask |= POLLIN | POLLRDNORM; + + DEBUGPOLL(if (mask != prev_mask) + printk("sync_serial_poll: mask 0x%08X %s %s\n", mask, + mask&POLLOUT?"POLLOUT":"", mask&POLLIN?"POLLIN":""); + prev_mask = mask; + ); + return mask; +} + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int return_val = 0; + unsigned long flags; + + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + sync_port* port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -1; + } + port = &ports[dev]; + + local_irq_save(flags); + /* Disable port while changing config */ + if (dev) + { + if (port->use_dma) { + RESET_DMA(4); WAIT_DMA(4); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + } + else + { + if (port->use_dma) { + RESET_DMA(8); WAIT_DMA(8); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + } + *R_GEN_CONFIG_II = gen_config_ii_shadow; + local_irq_restore(flags); + + switch(cmd) + { + case SSP_SPEED: + if (GET_SPEED(arg) == CODEC) + { + if (dev) + SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec); + else + SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec); + + SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, prescaler, GET_FREQ(arg)); + SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, frame_rate, GET_FRAME_RATE(arg)); + SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, word_rate, GET_WORD_RATE(arg)); + } + else + { + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_baud, GET_SPEED(arg)); + if (dev) + SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, baudrate); + else + SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, baudrate); + } + break; + case SSP_MODE: + if (arg > 5) + return -EINVAL; + if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT) + *R_IRQ_MASK1_CLR = 1 << port->data_avail_bit; + else if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg); + break; + case SSP_FRAME_SYNC: + if (arg & NORMAL_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal); + else if (arg & EARLY_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, early); + + if (arg & BIT_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, bit); + else if (arg & WORD_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word); + else if (arg & EXTENDED_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, extended); + + if (arg & SYNC_ON) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); + else if (arg & SYNC_OFF) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, off); + + if (arg & WORD_SIZE_8) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit); + else if (arg & WORD_SIZE_12) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size12bit); + else if (arg & WORD_SIZE_16) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size16bit); + else if (arg & WORD_SIZE_24) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size24bit); + else if (arg & WORD_SIZE_32) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size32bit); + + if (arg & BIT_ORDER_MSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb); + else if (arg & BIT_ORDER_LSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, lsb); + + if (arg & FLOW_CONTROL_ENABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled); + else if (arg & FLOW_CONTROL_DISABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled); + + if (arg & CLOCK_NOT_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, normal); + else if (arg & CLOCK_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, gated); + + break; + case SSP_IPOLARITY: + /* NOTE!! negedge is considered NORMAL */ + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, pos); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, inverted); + break; + case SSP_OPOLARITY: + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, normal); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, inverted); + break; + case SSP_SPI: + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal); + if (arg & SPI_SLAVE) + { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, SLAVE_INPUT); + } + else + { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, MASTER_OUTPUT); + } + break; + case SSP_INBUFCHUNK: +#if 0 + if (arg > port->in_buffer_size/NUM_IN_DESCR) + return -EINVAL; + port->inbufchunk = arg; + /* Make sure in_buffer_size is a multiple of inbufchunk */ + port->in_buffer_size = (port->in_buffer_size/port->inbufchunk) * port->inbufchunk; + DEBUG(printk("inbufchunk %i in_buffer_size: %i\n", port->inbufchunk, port->in_buffer_size)); + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } +#endif + break; + default: + return_val = -1; + } + /* Make sure we write the config without interruption */ + local_irq_save(flags); + /* Set config and enable port */ + *port->ctrl_data = port->ctrl_data_shadow; + nop(); nop(); nop(); nop(); + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow; + nop(); nop(); nop(); nop(); + if (dev) + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); + else + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); + + *R_GEN_CONFIG_II = gen_config_ii_shadow; + /* Reset DMA. At readout from serial port the data could be shifted + * one byte if not resetting DMA. + */ + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } + local_irq_restore(flags); + return return_val; +} + + +static ssize_t sync_serial_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + sync_port *port; + unsigned long flags; + unsigned long c, c1; + unsigned long free_outp; + unsigned long outp; + unsigned long out_buffer; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); + /* Space to end of buffer */ + /* + * out_buffer 012345<- c ->OUT_BUFFER_SIZE + * outp^ +out_count + ^free_outp + * out_buffer 45<- c ->0123OUT_BUFFER_SIZE + * +out_count outp^ + * free_outp + * + */ + + /* Read variables that may be updated by interrupts */ + local_irq_save(flags); + count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count; + outp = (unsigned long)port->outp; + free_outp = outp + port->out_count; + local_irq_restore(flags); + out_buffer = (unsigned long)port->out_buffer; + + /* Find out where and how much to write */ + if (free_outp >= out_buffer + OUT_BUFFER_SIZE) + free_outp -= OUT_BUFFER_SIZE; + if (free_outp >= outp) + c = out_buffer + OUT_BUFFER_SIZE - free_outp; + else + c = outp - free_outp; + if (c > count) + c = count; + +// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); + if (copy_from_user((void*)free_outp, buf, c)) + return -EFAULT; + + if (c != count) { + buf += c; + c1 = count - c; + DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1)); + if (copy_from_user((void*)out_buffer, buf, c1)) + return -EFAULT; + } + local_irq_save(flags); + port->out_count += count; + local_irq_restore(flags); + + /* Make sure transmitter/receiver is running */ + if (!port->started) + { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable); + port->started = 1; + } + + *port->ctrl_data = port->ctrl_data_shadow; + + if (file->f_flags & O_NONBLOCK) { + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit; + } else { + start_dma(port, (unsigned char* volatile )port->outp, c); + } + } + local_irq_restore(flags); + DEBUGWRITE(printk("w d%d c %lu NB\n", + port->port_nbr, count)); + return count; + } + + /* Sleep until all sent */ + + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit; + } else { + start_dma(port, port->outp, c); + } + } + local_irq_restore(flags); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) + { + return -EINTR; + } + DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count)); + return count; +} + +static ssize_t sync_serial_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + int avail; + sync_port *port; + unsigned char* start; + unsigned char* end; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + { + DEBUG(printk("Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGREAD(printk("R%d c %d ri %lu wi %lu /%lu\n", dev, count, port->readp - port->flip, port->writep - port->flip, port->in_buffer_size)); + + if (!port->started) + { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable); + port->started = 1; + } + *port->ctrl_data = port->ctrl_data_shadow; + + + /* Calculate number of available bytes */ + /* Save pointers to avoid that they are modified by interrupt */ + local_irq_save(flags); + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + local_irq_restore(flags); + while ((start == end) && !port->full) /* No data */ + { + if (file->f_flags & O_NONBLOCK) + { + return -EAGAIN; + } + + interruptible_sleep_on(&port->in_wait_q); + if (signal_pending(current)) + { + return -EINTR; + } + local_irq_save(flags); + start = (unsigned char*)port->readp; /* cast away volatile */ + end = (unsigned char*)port->writep; /* cast away volatile */ + local_irq_restore(flags); + } + + /* Lazy read, never return wrapped data. */ + if (port->full) + avail = port->in_buffer_size; + else if (end > start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + + count = count > avail ? avail : count; + if (copy_to_user(buf, start, count)) + return -EFAULT; + /* Disable interrupts while updating readp */ + local_irq_save(flags); + port->readp += count; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + port->full = 0; + local_irq_restore(flags); + DEBUGREAD(printk("r %d\n", count)); + return count; +} + +static void send_word(sync_port* port) +{ + switch(IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize, port->ctrl_data_shadow)) + { + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + port->out_count--; + *port->data_out = *port->outp++; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = (*port->outp++) << 8; + data |= *port->outp++; + port->out_count-=2; + *port->data_out = data; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + } + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + port->out_count-=2; + *port->data_out = *(unsigned short *)port->outp; + port->outp+=2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + port->out_count-=3; + *port->data_out = *(unsigned int *)port->outp; + port->outp+=3; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + port->out_count-=4; + *port->data_out = *(unsigned int *)port->outp; + port->outp+=4; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } +} + + +static void start_dma(struct sync_port* port, const char* data, int count) +{ + port->tr_running = 1; + port->out_descr.hw_len = 0; + port->out_descr.next = 0; + port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */ + port->out_descr.sw_len = count; + port->out_descr.buf = virt_to_phys((char*)data); + port->out_descr.status = 0; + + *port->output_dma_first = virt_to_phys(&port->out_descr); + *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); + DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); +} + +static void start_dma_in(sync_port* port) +{ + int i; + unsigned long buf; + port->writep = port->flip; + + if (port->writep > port->flip + port->in_buffer_size) + { + panic("Offset too large in sync serial driver\n"); + return; + } + buf = virt_to_phys(port->in_buffer); + for (i = 0; i < NUM_IN_DESCR; i++) { + port->in_descr[i].sw_len = port->inbufchunk; + port->in_descr[i].ctrl = d_int; + port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]); + port->in_descr[i].buf = buf; + port->in_descr[i].hw_len = 0; + port->in_descr[i].status = 0; + port->in_descr[i].fifo_len = 0; + buf += port->inbufchunk; + prepare_rx_descriptor(&port->in_descr[i]); + } + /* Link the last descriptor to the first */ + port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]); + port->in_descr[i-1].ctrl |= d_eol; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + *port->input_dma_first = virt_to_phys(port->next_rx_desc); + *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); +} + +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + int i; + struct etrax_dma_descr *descr; + unsigned int sentl; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port *port = &ports[i]; + if (!port->enabled || !port->use_dma ) + continue; + + if (ireg & (1 << port->output_dma_bit)) /* IRQ active for the port? */ + { + handled = 1; + + /* Clear IRQ */ + *port->output_dma_clr_irq = + IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); + + descr = &port->out_descr; + if (!(descr->status & d_stop)) { + sentl = descr->sw_len; + } else + /* otherwise we find the amount of data sent here */ + sentl = descr->hw_len; + port->out_count -= sentl; + port->outp += sentl; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + if (port->out_count) { + int c; + c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; + if (c > port->out_count) + c = port->out_count; + DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); + start_dma(port, port->outp, c); + } else { + DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); + port->tr_running = 0; + } + wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ + } + } + return IRQ_RETVAL(handled); +} /* tr_interrupt */ + +static irqreturn_t rx_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port *port = &ports[i]; + + if (!port->enabled || !port->use_dma ) + continue; + + if (ireg & (1 << port->input_dma_descr_bit)) /* Descriptor interrupt */ + { + handled = 1; + while (*port->input_dma_descr != virt_to_phys(port->next_rx_desc)) { + + if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { + int first_size = port->flip + port->in_buffer_size - port->writep; + memcpy(port->writep, phys_to_virt(port->next_rx_desc->buf), first_size); + memcpy(port->flip, phys_to_virt(port->next_rx_desc->buf+first_size), port->inbufchunk - first_size); + port->writep = port->flip + port->inbufchunk - first_size; + } else { + memcpy(port->writep, phys_to_virt(port->next_rx_desc->buf), port->inbufchunk); + port->writep += port->inbufchunk; + if (port->writep >= port->flip + port->in_buffer_size) + port->writep = port->flip; + } + if (port->writep == port->readp) + { + port->full = 1; + } + + prepare_rx_descriptor(port->next_rx_desc); + port->next_rx_desc->ctrl |= d_eol; + port->prev_rx_desc->ctrl &= ~d_eol; + port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); + wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ + *port->input_dma_cmd = IO_STATE(R_DMA_CH1_CMD, cmd, restart); + /* DMA has reached end of descriptor */ + *port->input_dma_clr_irq = + IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); + } + } + } + + return IRQ_RETVAL(handled); +} /* rx_interrupt */ +#endif /* SYNC_SER_DMA */ + +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id) +{ + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) + { + sync_port* port = &ports[i]; + + if (!port->enabled || port->use_dma) + { + continue; + } + + if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) /* Data received? */ + { + handled = 1; + /* Read data */ + switch(port->ctrl_data_shadow & IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) + { + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + *port->writep++ = *(volatile char *)port->data_in; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = *(unsigned short *)port->data_in; + *port->writep = (data & 0x0ff0) >> 4; + *(port->writep + 1) = data & 0x0f; + port->writep+=2; + } + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + *(unsigned short*)port->writep = *(volatile unsigned short *)port->data_in; + port->writep+=2; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + *(unsigned int*)port->writep = *port->data_in; + port->writep+=3; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + *(unsigned int*)port->writep = *port->data_in; + port->writep+=4; + break; + } + + if (port->writep >= port->flip + port->in_buffer_size) /* Wrap? */ + port->writep = port->flip; + if (port->writep == port->readp) { + /* receive buffer overrun, discard oldest data + */ + port->readp++; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + } + if (sync_data_avail(port) >= port->inbufchunk) + wake_up_interruptible(&port->in_wait_q); /* Wake up application */ + } + + if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) /* Transmitter ready? */ + { + if (port->out_count > 0) /* More data to send */ + send_word(port); + else /* transmission finished */ + { + *R_IRQ_MASK1_CLR = 1 << port->transmitter_ready_bit; /* Turn off IRQ */ + wake_up_interruptible(&port->out_wait_q); /* Wake up application */ + } + } + } + return IRQ_RETVAL(handled); +} +#endif + +module_init(etrax_sync_serial_init); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/debugport.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/debugport.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/debugport.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/debugport.c 2006-10-30 16:17:57.000000000 +0100 @@ -12,6 +12,34 @@ * init_etrax_debug() * * $Log: debugport.c,v $ + * Revision 1.36 2006/10/30 15:17:57 pkj + * Avoid a compiler warning. + * + * Revision 1.35 2006/10/13 12:43:11 starvik + * Merge of 2.6.18 + * + * Revision 1.34 2006/09/29 10:32:01 starvik + * Don't reference serial driver if not present + * + * Revision 1.33 2006/09/08 07:59:29 karljope + * Makes v10 boot again when watchdog is enabled + * + * Revision 1.32 2006/06/20 08:23:36 pkj + * Reverted incorrect merge. + * + * Revision 1.31 2006/05/17 12:22:10 edgar + * check port before disable ints + * + * Revision 1.30 2005/11/15 12:08:42 starvik + * Set index when no debug port is defined + * + * Revision 1.29 2005/08/29 07:32:17 starvik + * Merge of 2.6.13 + * + * Revision 1.28 2005/07/02 12:29:35 starvik + * Use the generic oops_in_progress instead of the raw_printk hack. + * Moved some functions to achr-independent code. + * * Revision 1.27 2005/06/10 10:34:14 starvik * Real console support * @@ -112,6 +140,8 @@ #include #include /* Get SIMCOUT. */ +extern void reset_watchdog(void); + struct dbg_port { unsigned int index; @@ -188,7 +218,9 @@ } }; +#ifdef CONFIG_ETRAX_SERIAL extern struct tty_driver *serial_driver; +#endif struct dbg_port* port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) @@ -368,11 +400,12 @@ { int i; unsigned long flags; - local_irq_save(flags); - + if (!port) return; - + + local_irq_save(flags); + /* Send data */ for (i = 0; i < len; i++) { /* LF -> CRLF */ @@ -386,26 +419,16 @@ ; *port->write = buf[i]; } - local_irq_restore(flags); -} -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - static int first = 1; - if (first) { - /* Force reinitialization of the port to get manual mode. */ - port->started = 0; - start_port(port); - first = 0; - } - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write_direct(NULL, buf, strlen(buf)); - return printed_len; + /* + * Feed the watchdog, otherwise it will reset the chip during boot. + * The time to send an ordinary boot message line (10-90 chars) + * varies between 1-8ms at 115200. What makes up for the additional + * 90ms that allows the watchdog to bite? + */ + reset_watchdog(); + + local_irq_restore(flags); } static void @@ -500,6 +523,7 @@ return 0; } + /* This is a dummy serial device that throws away anything written to it. * This is used when no debug output is wanted. */ @@ -555,7 +579,13 @@ { if (port) *index = port->index; + else + *index = 0; +#ifdef CONFIG_ETRAX_SERIAL return port ? serial_driver : &dummy_driver; +#else + return &dummy_driver; +#endif } static struct console sercons = { diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/entry.S linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/entry.S --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/entry.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/entry.S 2007-01-09 10:36:17.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $ +/* $Id: entry.S,v 1.38 2007/01/09 09:36:17 starvik Exp $ * * linux/arch/cris/entry.S * @@ -7,6 +7,41 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: entry.S,v $ + * Revision 1.38 2007/01/09 09:36:17 starvik + * Corrected kernel_execve + * + * Revision 1.37 2007/01/09 09:29:18 starvik + * Merge of Linux 2.6.19 + * + * Revision 1.36 2006/12/08 13:08:54 orjanf + * Copied from Linux 2.4: + * * Return from an pin-generated NMI the same way as for other interrupts. + * * Reverse check order between external nmi and watchdog nmi to avoid false + * watchdog oops in case of a glitch on the nmi pin. + * + * Revision 1.35 2006/10/13 12:43:11 starvik + * Merge of 2.6.18 + * + * Revision 1.34 2006/06/25 15:00:09 starvik + * Merge of Linux 2.6.17 + * + * Revision 1.33 2006/05/19 12:23:09 orjanf + * * Moved blocking of ethernet rx/tx irq from ethernet interrupt handler to + * low-level asm interrupt handlers. Fixed in the multiple interrupt handler + * also. Copied from Linux 2.4. + * + * Revision 1.32 2006/03/23 14:53:57 starvik + * Corrected signal handling. + * + * Revision 1.31 2006/03/22 09:56:55 starvik + * Merge of Linux 2.6.16 + * + * Revision 1.30 2005/10/31 08:48:03 starvik + * Merge of Linux 2.6.14 + * + * Revision 1.29 2005/08/29 07:32:17 starvik + * Merge of 2.6.13 + * * Revision 1.28 2005/06/20 05:06:30 starvik * Remove unnecessary diff to kernel.org tree * @@ -500,9 +535,8 @@ ;; deal with pending signals and notify-resume requests move.d $r9, $r10 ; do_notify_resume syscall/irq param - moveq 0, $r11 ; oldset param - 0 in this case - move.d $sp, $r12 ; the regs param - move.d $r1, $r13 ; the thread_info_flags parameter + move.d $sp, $r11 ; the regs param + move.d $r1, $r12 ; the thread_info_flags parameter jsr do_notify_resume ba _Rexit @@ -653,7 +687,7 @@ ;; special handlers for breakpoint and NMI hwbreakpoint: push $dccr - di + di push $r10 push $r11 move.d [hw_bp_trig_ptr],$r10 @@ -678,13 +712,19 @@ push $r10 ; push orig_r10 clear.d [$sp=$sp-4] ; frametype == 0, normal frame + ;; If there is a glitch on the NMI pin shorter than ~100ns + ;; (i.e. non-active by the time we get here) then the nmi_pin bit + ;; in R_IRQ_MASK0_RD will already be cleared. The watchdog_nmi bit + ;; is cleared by us however (when feeding the watchdog), which is why + ;; we use that bit to determine what brought us here. + move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog? - and.d 0x80000000, $r1 - beq wdog + and.d (1<<30), $r1 + bne wdog move.d $sp, $r10 jsr handle_nmi setf m ; Enable NMI again - retb ; Return from NMI + ba _Rexit ; Return the standard way nop wdog: #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) @@ -774,23 +814,10 @@ movem $r13, [$sp] push $r10 ; push orig_r10 clear.d [$sp=$sp-4] ; frametype == 0, normal frame - - moveq 2, $r2 ; first bit we care about is the timer0 irq - move.d [R_VECT_MASK_RD], $r0; read the irq bits that triggered the multiple irq - move.d $r0, [R_VECT_MASK_CLR] ; Block all active IRQs -1: - btst $r2, $r0 ; check for the irq given by bit r2 - bpl 2f - move.d $r2, $r10 ; First argument to do_IRQ - move.d $sp, $r11 ; second argument to do_IRQ - jsr do_IRQ -2: - addq 1, $r2 ; next vector bit - cmp.b 32, $r2 - bne 1b ; process all irq's up to and including number 31 - moveq 0, $r9 ; make ret_from_intr realise we came from an ir - - move.d $r0, [R_VECT_MASK_SET] ; Unblock all the IRQs + + move.d $sp, $r10 + jsr do_multiple_IRQ + jump ret_from_intr do_sigtrap: @@ -836,6 +863,13 @@ pop $r0 ; Restore r0. ba do_sigtrap ; SIGTRAP the offending process. pop $dccr ; Restore dccr in delay slot. + + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop .data @@ -1135,7 +1169,38 @@ .long sys_add_key .long sys_request_key .long sys_keyctl - + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + /* * NOTE!! This doesn't have to be exact - we just have * to make sure we have _enough_ of the "sys_ni_syscall" diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/fasttimer.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/fasttimer.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/fasttimer.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/fasttimer.c 2007-02-05 12:54:34.000000000 +0100 @@ -1,97 +1,10 @@ -/* $Id: fasttimer.c,v 1.9 2005/03/04 08:16:16 starvik Exp $ +/* * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX100/ETRAX100LX * This may be useful in other OS than Linux so use 2 space indentation... * - * $Log: fasttimer.c,v $ - * Revision 1.9 2005/03/04 08:16:16 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.8 2005/01/05 06:09:29 starvik - * cli()/sti() will be obsolete in 2.6.11. - * - * Revision 1.7 2005/01/03 13:35:46 starvik - * Removed obsolete stuff. - * Mark fast timer IRQ as not shared. - * - * Revision 1.6 2004/05/14 10:18:39 starvik - * Export fast_timer_list - * - * Revision 1.5 2004/05/14 07:58:01 starvik - * Merge of changes from 2.4 - * - * Revision 1.4 2003/07/04 08:27:41 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.3 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.2 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.1 2002/11/18 07:58:06 starvik - * Fast timers (from Linux 2.4) - * - * Revision 1.5 2002/10/15 06:21:39 starvik - * Added call to init_waitqueue_head - * - * Revision 1.4 2002/05/28 17:47:59 johana - * Added del_fast_timer() - * - * Revision 1.3 2002/05/28 16:16:07 johana - * Handle empty fast_timer_list - * - * Revision 1.2 2002/05/27 15:38:42 johana - * Made it compile without warnings on Linux 2.4. - * (includes, wait_queue, PROC_FS and snprintf) - * - * Revision 1.1 2002/05/27 15:32:25 johana - * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. - * - * Revision 1.8 2001/11/27 13:50:40 pkj - * Disable interrupts while stopping the timer and while modifying the - * list of active timers in timer1_handler() as it may be interrupted - * by other interrupts (e.g., the serial interrupt) which may add fast - * timers. - * - * Revision 1.7 2001/11/22 11:50:32 pkj - * * Only store information about the last 16 timers. - * * proc_fasttimer_read() now uses an allocated buffer, since it - * requires more space than just a page even for only writing the - * last 16 timers. The buffer is only allocated on request, so - * unless /proc/fasttimer is read, it is never allocated. - * * Renamed fast_timer_started to fast_timers_started to match - * fast_timers_added and fast_timers_expired. - * * Some clean-up. - * - * Revision 1.6 2000/12/13 14:02:08 johana - * Removed volatile for fast_timer_list - * - * Revision 1.5 2000/12/13 13:55:35 johana - * Added DEBUG_LOG, added som cli() and cleanup - * - * Revision 1.4 2000/12/05 13:48:50 johana - * Added range check when writing proc file, modified timer int handling - * - * Revision 1.3 2000/11/23 10:10:20 johana - * More debug/logging possibilities. - * Moved GET_JIFFIES_USEC() to timex.h and time.c - * - * Revision 1.2 2000/11/01 13:41:04 johana - * Clean up and bugfixes. - * Created new do_gettimeofday_fast() that gets a timeval struct - * with time based on jiffies and *R_TIMER0_DATA, uses a table - * for fast conversion of timer value to microseconds. - * (Much faster the standard do_gettimeofday() and we don't really - * wan't to use the true time - we wan't the "uptime" so timers don't screw up - * when we change the time. - * TODO: Add efficient support for continuous timers as well. - * - * Revision 1.1 2000/10/26 15:49:16 johana - * Added fasttimer, highresolution timers. - * - * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden */ #include @@ -136,13 +49,13 @@ #define __INLINE__ inline -static int fast_timer_running = 0; -static int fast_timers_added = 0; -static int fast_timers_started = 0; -static int fast_timers_expired = 0; -static int fast_timers_deleted = 0; -static int fast_timer_is_init = 0; -static int fast_timer_ints = 0; +static unsigned int fast_timer_running = 0; +static unsigned int fast_timers_added = 0; +static unsigned int fast_timers_started = 0; +static unsigned int fast_timers_expired = 0; +static unsigned int fast_timers_deleted = 0; +static unsigned int fast_timer_is_init = 0; +static unsigned int fast_timer_ints = 0; struct fast_timer *fast_timer_list = NULL; @@ -150,8 +63,8 @@ #define DEBUG_LOG_MAX 128 static const char * debug_log_string[DEBUG_LOG_MAX]; static unsigned long debug_log_value[DEBUG_LOG_MAX]; -static int debug_log_cnt = 0; -static int debug_log_cnt_wrapped = 0; +static unsigned int debug_log_cnt = 0; +static unsigned int debug_log_cnt_wrapped = 0; #define DEBUG_LOG(string, value) \ { \ @@ -206,41 +119,25 @@ int timer_delay_settings[NUM_TIMER_STATS]; /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ -void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +void __INLINE__ do_gettimeofday_fast(struct fasttime_t *tv) { - unsigned long sec = jiffies; - unsigned long usec = GET_JIFFIES_USEC(); - - usec += (sec % HZ) * (1000000 / HZ); - sec = sec / HZ; - - if (usec > 1000000) - { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_jiff = jiffies; + tv->tv_usec = GET_JIFFIES_USEC(); } -int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +int __INLINE__ timeval_cmp(struct fasttime_t *t0, struct fasttime_t *t1) { - if (t0->tv_sec < t1->tv_sec) - { + /* Compare jiffies. Takes care of wrapping */ + if (time_before(t0->tv_jiff, t1->tv_jiff)) return -1; - } - else if (t0->tv_sec > t1->tv_sec) - { + else if (time_after(t0->tv_jiff, t1->tv_jiff)) return 1; - } + + /* Compare us */ if (t0->tv_usec < t1->tv_usec) - { return -1; - } else if (t0->tv_usec > t1->tv_usec) - { return 1; - } return 0; } @@ -340,7 +237,7 @@ printk(KERN_WARNING "timer name: %s data: 0x%08lX already in list!\n", name, data); sanity_failed++; - return; + goto done; } else { @@ -356,11 +253,11 @@ t->name = name; t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; - t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; + t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ; if (t->tv_expires.tv_usec > 1000000) { t->tv_expires.tv_usec -= 1000000; - t->tv_expires.tv_sec++; + t->tv_expires.tv_jiff += HZ; } #ifdef FAST_TIMER_LOG timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; @@ -401,6 +298,7 @@ D2(printk("start_one_shot_timer: %d us done\n", delay_us)); +done: local_irq_restore(flags); } /* start_one_shot_timer */ @@ -444,11 +342,18 @@ /* Timer 1 interrupt handler */ static irqreturn_t -timer1_handler(int irq, void *dev_id, struct pt_regs *regs) +timer1_handler(int irq, void *dev_id) { struct fast_timer *t; unsigned long flags; + /* We keep interrupts disabled not only when we modify the + * fast timer list, but any time we hold a reference to a + * timer in the list, since del_fast_timer may be called + * from (another) interrupt context. Thus, the only time + * when interrupts are enabled is when calling the timer + * callback function. + */ local_irq_save(flags); /* Clear timer1 irq */ @@ -466,16 +371,16 @@ fast_timer_running = 0; fast_timer_ints++; - local_irq_restore(flags); - t = fast_timer_list; while (t) { - struct timeval tv; + struct fasttime_t tv; + fast_timer_function_type *f; + unsigned long d; /* Has it really expired? */ do_gettimeofday_fast(&tv); - D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + D1(printk("t: %is %06ius\n", tv.tv_jiff, tv.tv_usec)); if (timeval_cmp(&t->tv_expires, &tv) <= 0) { @@ -486,7 +391,6 @@ fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - local_irq_save(flags); if (t->prev) { t->prev->next = t->next; @@ -501,11 +405,21 @@ } t->prev = NULL; t->next = NULL; - local_irq_restore(flags); - if (t->function != NULL) + /* Save function callback data before enabling interrupts, + * since the timer may be removed and we don't know how it + * was allocated (e.g. ->function and ->data may become + * overwritten after deletion if the timer was stack-allocated). + */ + f = t->function; + d = t->data; + + if (f != NULL) { - t->function(t->data); + /* Run the callback function with interrupts enabled. */ + local_irq_restore(flags); + f(d); + local_irq_save(flags); } else { @@ -518,16 +432,19 @@ D1(printk(".\n")); } - local_irq_save(flags); if ((t = fast_timer_list) != NULL) { /* Start next timer.. */ - long us; - struct timeval tv; + long us = 0; + struct fasttime_t tv; do_gettimeofday_fast(&tv); - us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + - t->tv_expires.tv_usec - tv.tv_usec); + + /* time_after_eq takes care of wrapping */ + if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff)) + us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * 1000000 / HZ + + t->tv_expires.tv_usec - tv.tv_usec); + if (us > 0) { if (!fast_timer_running) @@ -537,7 +454,6 @@ #endif start_timer1(us); } - local_irq_restore(flags); break; } else @@ -548,9 +464,10 @@ D1(printk("e! %d\n", us)); } } - local_irq_restore(flags); } + local_irq_restore(flags); + if (!t) { D1(printk("t1 stop!\n")); @@ -575,28 +492,17 @@ void schedule_usleep(unsigned long us) { struct fast_timer t; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t sleep_wait; init_waitqueue_head(&sleep_wait); - { - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue *sleep_wait = NULL; - struct wait_queue wait = { current, NULL }; -#endif D1(printk("schedule_usleep(%d)\n", us)); - add_wait_queue(&sleep_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, "usleep"); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&sleep_wait, &wait); + /* Uninterruptible sleep on the fast timer. (The condition is somewhat + redundant since the timer is what wakes us up.) */ + wait_event(sleep_wait, !fast_timer_pending(&t)); + D1(printk("done schedule_usleep(%d)\n", us)); -#ifdef DECLARE_WAITQUEUE - } -#endif } #ifdef CONFIG_PROC_FS @@ -616,7 +522,7 @@ unsigned long flags; int i = 0; int num_to_show; - struct timeval tv; + struct fasttime_t tv; struct fast_timer *t, *nextt; static char *bigbuf = NULL; static unsigned long used; @@ -624,7 +530,8 @@ if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) { used = 0; - bigbuf[0] = '\0'; + if (buf) + buf[0] = '\0'; return 0; } @@ -646,7 +553,7 @@ used += sprintf(bigbuf + used, "Fast timer running: %s\n", fast_timer_running ? "yes" : "no"); used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", - (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_jiff, (unsigned long)tv.tv_usec); #ifdef FAST_TIMER_SANITY_CHECKS used += sprintf(bigbuf + used, "Sanity failed: %i\n", @@ -696,9 +603,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -718,9 +625,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -738,9 +645,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -761,15 +668,15 @@ /* " func: 0x%08lX" */ "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data /* , t->function */ ); - local_irq_disable(); + local_irq_save(flags); if (t->next != nextt) { printk(KERN_WARNING "timer removed!\n"); @@ -798,7 +705,7 @@ static struct fast_timer tr[10]; static int exp_num[10]; -static struct timeval tv_exp[100]; +static struct fasttime_t tv_exp[100]; static void test_timeout(unsigned long data) { @@ -836,7 +743,7 @@ int prev_num; int j; - struct timeval tv, tv0, tv1, tv2; + struct fasttime_t tv, tv0, tv1, tv2; printk("fast_timer_test() start\n"); do_gettimeofday_fast(&tv); @@ -849,7 +756,7 @@ { do_gettimeofday_fast(&tv_exp[j]); } - printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + printk("fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec); for (j = 0; j < 1000; j++) { @@ -859,11 +766,11 @@ for (j = 0; j < 100; j++) { printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", - tv_exp[j].tv_sec,tv_exp[j].tv_usec, - tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, - tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, - tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, - tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + tv_exp[j].tv_jiff,tv_exp[j].tv_usec, + tv_exp[j+1].tv_jiff,tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_jiff,tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_jiff,tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_jiff,tv_exp[j+4].tv_usec); j += 4; } do_gettimeofday_fast(&tv0); @@ -895,9 +802,9 @@ } } do_gettimeofday_fast(&tv2); - printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); - printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); - printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + printk("Timers started %is %06i\n", tv0.tv_jiff, tv0.tv_usec); + printk("Timers started at %is %06i\n", tv1.tv_jiff, tv1.tv_usec); + printk("Timers done %is %06i\n", tv2.tv_jiff, tv2.tv_usec); DP(printk("buf0:\n"); printk(buf0); printk("buf1:\n"); @@ -919,9 +826,9 @@ printk("%-10s set: %6is %06ius exp: %6is %06ius " "data: 0x%08X func: 0x%08X\n", t->name, - t->tv_set.tv_sec, + t->tv_set.tv_jiff, t->tv_set.tv_usec, - t->tv_expires.tv_sec, + t->tv_expires.tv_jiff, t->tv_expires.tv_usec, t->data, t->function @@ -929,10 +836,10 @@ printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", t->delay_us, - tv_exp[j].tv_sec, + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, exp_num[j], - (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); } proc_fasttimer_read(buf5, NULL, 0, 0, 0); printk("buf5 after all done:\n"); @@ -942,7 +849,7 @@ #endif -void fast_timer_init(void) +int fast_timer_init(void) { /* For some reason, request_irq() hangs when called froom time_init() */ if (!fast_timer_is_init) @@ -975,4 +882,6 @@ fast_timer_test(); #endif } + return 0; } +__initcall(fast_timer_init); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/head.S linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/head.S --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/head.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/head.S 2006-10-20 09:33:26.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $ +/* $Id: head.S,v 1.13 2006/10/20 07:33:26 kjelld Exp $ * * Head of the kernel - alter with care * @@ -7,6 +7,19 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: head.S,v $ + * Revision 1.13 2006/10/20 07:33:26 kjelld + * If serial port 2 is used, select it in R_GEN_CONFIG. + * If serial port 2 is used, setup the control registers for the port. + * This is done to avoid a puls on the TXD line during start up. + * This puls could disturbe some units (e.g. some Axis 214 with internal + * ver. 1.08). + * + * Revision 1.12 2006/10/13 12:43:11 starvik + * Merge of 2.6.18 + * + * Revision 1.11 2005/08/29 07:32:17 starvik + * Merge of 2.6.13 + * * Revision 1.10 2005/06/20 05:12:54 starvik * Remove unnecessary diff to kernel.org tree * @@ -595,11 +608,17 @@ moveq 0,$r0 + ;; Select or disable serial port 2 +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + or.d IO_STATE (R_GEN_CONFIG, ser2, select),$r0 +#else + or.d IO_STATE (R_GEN_CONFIG, ser2, disable),$r0 +#endif + ;; Init interfaces (disable them). or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \ | IO_STATE (R_GEN_CONFIG, ata, disable) \ | IO_STATE (R_GEN_CONFIG, par0, disable) \ - | IO_STATE (R_GEN_CONFIG, ser2, disable) \ | IO_STATE (R_GEN_CONFIG, mio, disable) \ | IO_STATE (R_GEN_CONFIG, scsi1, disable) \ | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \ @@ -801,6 +820,41 @@ | IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0 move.b $r0,[R_SERIAL1_TR_CTRL] +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + ;; setup the serial port 2 at 115200 baud for debug purposes + + moveq IO_STATE (R_SERIAL2_XOFF, tx_stop, enable) \ + | IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable) \ + | IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0 + move.d $r0,[R_SERIAL2_XOFF] + + ; 115.2kbaud for both transmit and receive + move.b IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz) \ + | IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0 + move.b $r0,[R_SERIAL2_BAUD] + + ; Set up and enable the serial2 receiver. + move.b IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rts_, active) \ + | IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0 + move.b $r0,[R_SERIAL2_REC_CTRL] + + ; Set up and enable the serial2 transmitter. + move.b IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled) \ + | IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0 + move.b $r0,[R_SERIAL2_TR_CTRL] +#endif #ifdef CONFIG_ETRAX_SERIAL_PORT3 ;; setup the serial port 3 at 115200 baud for debug purposes diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/io_interface_mux.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/io_interface_mux.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/io_interface_mux.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/io_interface_mux.c 2006-10-04 20:21:18.000000000 +0200 @@ -1,10 +1,10 @@ /* IO interface mux allocator for ETRAX100LX. - * Copyright 2004, Axis Communications AB - * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $ + * Copyright 2004-2006, Axis Communications AB + * $Id: io_interface_mux.c,v 1.4 2006/10/04 18:21:18 henriken Exp $ */ -/* C.f. ETRAX100LX Designer's Reference 20.9 */ +/* C.f. ETRAX100LX Designer's Reference 19.9 */ #include #include @@ -35,7 +35,7 @@ struct watcher { void (*notify)(const unsigned int gpio_in_available, - const unsigned int gpio_out_available, + const unsigned int gpio_out_available, const unsigned char pa_available, const unsigned char pb_available); struct watcher *next; @@ -45,17 +45,40 @@ struct if_group { enum io_if_group group; - unsigned char used; - enum cris_io_interface owner; + // name - the name of the group 'A' to 'F' + char *name; + // used - a bit mask of all pins in the group in the order listed + // in in the tables in 19.9.1 to 19.9.6. Note that no + // distinction is made between in, out and in/out pins. + unsigned int used; }; struct interface { enum cris_io_interface ioif; + // name - the name of the interface + char *name; + // groups - OR'ed together io_if_group flags describing what pin groups + // the interface uses pins in. unsigned char groups; + // used - set when the interface is allocated. unsigned char used; char *owner; + // group_a through group_f - bit masks describing what pins in the + // pin groups the interface uses. + unsigned int group_a; + unsigned int group_b; + unsigned int group_c; + unsigned int group_d; + unsigned int group_e; + unsigned int group_f; + + // gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the + // GPIO ports the interface uses. This + // could be reconstucted using the group_X + // masks and a table of what pins the GPIO + // ports use, but that would be messy. unsigned int gpio_g_in; unsigned int gpio_g_out; unsigned char gpio_b; @@ -64,26 +87,32 @@ static struct if_group if_groups[6] = { { .group = group_a, + .name = "A", .used = 0, }, { .group = group_b, + .name = "B", .used = 0, }, { .group = group_c, + .name = "C", .used = 0, }, { .group = group_d, + .name = "D", .used = 0, }, { .group = group_e, + .name = "E", .used = 0, }, { .group = group_f, + .name = "F", .used = 0, } }; @@ -94,14 +123,32 @@ /* Begin Non-multiplexed interfaces */ { .ioif = if_eth, + .name = "ethernet", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 }, { .ioif = if_serial_0, + .name = "serial_0", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 @@ -109,172 +156,385 @@ /* End Non-multiplexed interfaces */ { .ioif = if_serial_1, + .name = "serial_1", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_serial_2, + .name = "serial_2", .groups = group_b, + + .group_a = 0, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_serial_3, + .name = "serial_3", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_sync_serial_1, - .groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3 - can be used simultaneously */ + .name = "sync_serial_1", + .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x10, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x10 }, { .ioif = if_sync_serial_3, + .name = "sync_serial_3", .groups = group_c | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x80 }, { .ioif = if_shared_ram, + .name = "shared_ram", .groups = group_a, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff38, .gpio_b = 0x00 }, { .ioif = if_shared_ram_w, + .name = "shared_ram_w", .groups = group_a | group_d, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff38, .gpio_b = 0x00 }, { .ioif = if_par_0, + .name = "par_0", .groups = group_a, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff3e, .gpio_b = 0x00 }, { .ioif = if_par_1, + .name = "par_1", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0x7feff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3eff0000, .gpio_g_out = 0x3eff0000, .gpio_b = 0x00 }, { .ioif = if_par_w, + .name = "par_w", .groups = group_a | group_d, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff3e, .gpio_b = 0x00 }, { .ioif = if_scsi8_0, - .groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_0", + .groups = group_a | group_b | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x10, + .gpio_g_in = 0x0000ffff, .gpio_g_out = 0x0000ffff, .gpio_b = 0x10 }, { .ioif = if_scsi8_1, - .groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_1", + .groups = group_c | group_d | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0x7ffff, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xffff0000, .gpio_g_out = 0xffff0000, .gpio_b = 0x80 }, { .ioif = if_scsi_w, + .name = "scsi_w", .groups = group_a | group_b | group_d | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0x601ff, + .group_e = 0, + .group_f = 0x90, + .gpio_g_in = 0x01ffffff, .gpio_g_out = 0x07ffffff, .gpio_b = 0x80 }, { .ioif = if_ata, + .name = "ata", .groups = group_a | group_b | group_c | group_d, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0x0f, + .group_d = 0x7cfff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xf9ffffff, .gpio_g_out = 0xffffffff, .gpio_b = 0x80 }, { .ioif = if_csp, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "csp", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0xfc, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xfc }, { .ioif = if_i2c, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "i2c", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x03, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x03 }, { .ioif = if_usb_1, + .name = "usb_1", .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x2c, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x2c }, { .ioif = if_usb_2, + .name = "usb_2", .groups = group_d, - .gpio_g_in = 0x0e000000, - .gpio_g_out = 0x3c000000, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x33e00, + .group_f = 0, + + .gpio_g_in = 0x3e000000, + .gpio_g_out = 0x0c000000, .gpio_b = 0x00 }, /* GPIO pins */ { .ioif = if_gpio_grp_a, + .name = "gpio_a", .groups = group_a, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3f, .gpio_g_out = 0x0000ff3f, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_b, + .name = "gpio_b", .groups = group_b, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_c, + .name = "gpio_c", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_d, + .name = "gpio_d", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3fff0000, .gpio_g_out = 0x3fff0000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_e, + .name = "gpio_e", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_f, + .name = "gpio_f", .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xff @@ -284,11 +544,13 @@ static struct watcher *watchers = NULL; +// The pins that are free to use in the GPIO ports. static unsigned int gpio_in_pins = 0xffffffff; static unsigned int gpio_out_pins = 0xffffffff; static unsigned char gpio_pb_pins = 0xff; static unsigned char gpio_pa_pins = 0xff; +// Identifiers for the owners of the GPIO pins. static enum cris_io_interface gpio_pa_owners[8]; static enum cris_io_interface gpio_pb_owners[8]; static enum cris_io_interface gpio_pg_owners[32]; @@ -318,7 +580,7 @@ struct watcher *w = watchers; DBG(printk("io_interface_mux: notifying watchers\n")); - + while (NULL != w) { w->notify((const unsigned int)gpio_in_pins, (const unsigned int)gpio_out_pins, @@ -354,37 +616,48 @@ if (interfaces[ioif].used) { local_irq_restore(flags); - printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n", + printk(KERN_CRIT "cris_io_interface: Cannot allocate interface %s for %s, in use by %s\n", + interfaces[ioif].name, device_id, interfaces[ioif].owner); return -EBUSY; } - /* Check that all required groups are free before allocating, */ + /* Check that all required pins in the used groups are free + * before allocating. */ group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->used) { - if (grp->group == group_f) { - if ((if_sync_serial_1 == ioif) || - (if_sync_serial_3 == ioif)) { - if ((grp->owner != if_sync_serial_1) && - (grp->owner != if_sync_serial_3)) { - local_irq_restore(flags); - return -EBUSY; - } - } else if ((if_scsi8_0 == ioif) || - (if_scsi8_1 == ioif)) { - if ((grp->owner != if_scsi8_0) && - (grp->owner != if_scsi8_1)) { - local_irq_restore(flags); - return -EBUSY; - } - } - } else { - local_irq_restore(flags); - return -EBUSY; - } + unsigned int if_group_use = 0; + + switch(grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if(if_group_use & grp->used) { + local_irq_restore(flags); + printk(KERN_INFO "cris_request_io_interface: group %s needed by %s not available\n", grp->name, interfaces[ioif].name); + return -EBUSY; + } + group_set = clear_group_from_set(group_set, grp); } @@ -392,22 +665,16 @@ if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) || ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) || ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) { - printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n", - ioif); + local_irq_restore(flags); + printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %s\n", + interfaces[ioif].name); return -EBUSY; } - /* All needed I/O pins and pin groups are free, allocate. */ - group_set = interfaces[ioif].groups; - while (NULL != (grp = get_group(group_set))) { - grp->used = 1; - grp->owner = ioif; - group_set = clear_group_from_set(group_set, grp); - } - + /* Check which registers need to be reconfigured. */ gens = genconfig_shadow; gens_ii = gen_config_ii_shadow; - + set_gen_config = 1; switch (ioif) { @@ -494,9 +761,43 @@ set_gen_config = 0; break; default: - panic("cris_request_io_interface: Bad interface %u submitted for %s\n", - ioif, - device_id); + local_irq_restore(flags); + printk(KERN_INFO "cris_request_io_interface: Bad interface %u submitted for %s\n", + ioif, + device_id); + return -EBUSY; + } + + /* All needed I/O pins and pin groups are free, allocate. */ + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + unsigned int if_group_use = 0; + + switch(grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); + } + grp->used |= if_group_use; + + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 1; @@ -528,7 +829,7 @@ DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", gpio_in_pins, gpio_out_pins, gpio_pb_pins)); - + local_irq_restore(flags); notify_watchers(); @@ -559,43 +860,36 @@ } group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->group == group_f) { - switch (ioif) - { - case if_sync_serial_1: - if ((grp->owner == if_sync_serial_1) && - interfaces[if_sync_serial_3].used) { - grp->owner = if_sync_serial_3; - } else - grp->used = 0; - break; - case if_sync_serial_3: - if ((grp->owner == if_sync_serial_3) && - interfaces[if_sync_serial_1].used) { - grp->owner = if_sync_serial_1; - } else - grp->used = 0; - break; - case if_scsi8_0: - if ((grp->owner == if_scsi8_0) && - interfaces[if_scsi8_1].used) { - grp->owner = if_scsi8_1; - } else - grp->used = 0; - break; - case if_scsi8_1: - if ((grp->owner == if_scsi8_1) && - interfaces[if_scsi8_0].used) { - grp->owner = if_scsi8_0; - } else - grp->used = 0; - break; - default: - grp->used = 0; - } - } else { - grp->used = 0; + unsigned int if_group_use = 0; + + switch(grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if ((grp->used & if_group_use) != if_group_use) { + BUG_ON(1); + } + grp->used = grp->used & ~if_group_use; + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 0; @@ -784,7 +1078,7 @@ for (i = start_bit; i <= stop_bit; i++) { owners[i] = if_unclaimed; - } + } local_irq_restore(flags); notify_watchers(); @@ -821,7 +1115,7 @@ } void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available, - const unsigned int gpio_out_available, + const unsigned int gpio_out_available, const unsigned char pa_available, const unsigned char pb_available)) { @@ -870,7 +1164,7 @@ module_init(cris_io_interface_init); - + EXPORT_SYMBOL(cris_request_io_interface); EXPORT_SYMBOL(cris_free_io_interface); EXPORT_SYMBOL(cris_io_interface_allocate_pins); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/irq.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/irq.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/irq.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/irq.c 2006-10-30 16:17:03.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $ +/* $Id: irq.c,v 1.9 2006/10/30 15:17:03 pkj Exp $ * * linux/arch/cris/kernel/irq.c * @@ -12,7 +12,9 @@ */ #include +#include #include +#include #include #include @@ -75,8 +77,8 @@ BUILD_IRQ(13, 0x2000) void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ -BUILD_IRQ(16, 0x10000) -BUILD_IRQ(17, 0x20000) +BUILD_IRQ(16, 0x10000 | 0x20000) /* ethernet tx interrupt needs to block rx */ +BUILD_IRQ(17, 0x20000 | 0x10000) /* ...and vice versa */ BUILD_IRQ(18, 0x40000) BUILD_IRQ(19, 0x80000) BUILD_IRQ(20, 0x100000) @@ -147,6 +149,55 @@ void do_sigtrap(void); /* from entry.S */ void gdb_handle_breakpoint(void); /* from entry.S */ +extern void do_IRQ(int irq, struct pt_regs * regs); + +/* Handle multiple IRQs */ +void do_multiple_IRQ(struct pt_regs* regs) +{ + int bit; + unsigned masked; + unsigned mask; + unsigned ethmask = 0; + + /* Get interrupts to mask and handle */ + mask = masked = *R_VECT_MASK_RD; + + /* Never mask timer IRQ */ + mask &= ~(IO_MASK(R_VECT_MASK_RD, timer0)); + + /* + * If either ethernet interrupt (rx or tx) is active then block + * the other one too. Unblock afterwards also. + */ + if (mask & + (IO_STATE(R_VECT_MASK_RD, dma0, active) | + IO_STATE(R_VECT_MASK_RD, dma1, active))) { + ethmask = (IO_MASK(R_VECT_MASK_RD, dma0) | + IO_MASK(R_VECT_MASK_RD, dma1)); + } + + /* Block them */ + *R_VECT_MASK_CLR = (mask | ethmask); + + /* An extra irq_enter here to prevent softIRQs to run after + * each do_IRQ. This will decrease the interrupt latency. + */ + irq_enter(); + + /* Handle all IRQs */ + for (bit = 2; bit < 32; bit++) { + if (masked & (1 << bit)) { + do_IRQ(bit, regs); + } + } + + /* This irq_exit() will trigger the soft IRQs. */ + irq_exit(); + + /* Unblock the IRQs again */ + *R_VECT_MASK_SET = (masked | ethmask); +} + /* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and setting the irq vector table. */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/kgdb.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/kgdb.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/kgdb.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/kgdb.c 2006-03-22 10:56:55.000000000 +0100 @@ -18,6 +18,10 @@ *! Jul 21 1999 Bjorn Wesen eLinux port *! *! $Log: kgdb.c,v $ +*! Revision 1.7 2006/03/22 09:56:55 starvik +*! Merge of Linux 2.6.16 +*! +*! *! Revision 1.6 2005/01/14 10:12:17 starvik *! KGDB on separate port. *! Console fixes from 2.4. @@ -75,7 +79,7 @@ *! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $ +*! $Id: kgdb.c,v 1.7 2006/03/22 09:56:55 starvik Exp $ *! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/process.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/process.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/process.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/process.c 2006-10-13 14:43:11.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $ +/* $Id: process.c,v 1.14 2006/10/13 12:43:11 starvik Exp $ * * linux/arch/cris/kernel/process.c * diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/ptrace.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/ptrace.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/ptrace.c 2006-10-30 16:17:57.000000000 +0100 @@ -66,6 +66,7 @@ ptrace_disable(struct task_struct *child) { /* Todo - pending singlesteps? */ + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } /* diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/setup.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/setup.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/setup.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/setup.c 2006-10-13 14:43:11.000000000 +0200 @@ -1,4 +1,4 @@ -/* +/* * * linux/arch/cris/arch-v10/kernel/setup.c * @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef CONFIG_PROC_FS #define HAS_FPU 0x0001 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/signal.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/signal.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/signal.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/signal.c 2006-03-22 10:56:55.000000000 +0100 @@ -41,7 +41,7 @@ */ #define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2; -int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); +void do_signal(int canrestart, struct pt_regs *regs); /* * Atomically swap in the new signal mask, and wait for a signal. Define @@ -52,68 +52,16 @@ sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - sigset_t saveset; - mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(0, &saveset, regs)) - /* We will get here twice: once to call the signal - handler, then again to return from the - sigsuspend system call. When calling the - signal handler, R10 holds the signal number as - set through do_signal. The sigsuspend call - will return with the restored value set above; - always -EINTR. */ - return regs->r10; - } -} - -/* Define dummy arguments to be able to reach the regs argument. (Note that - * this arrangement relies on size_t occupying one register.) - */ -int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, - long mof, long srp, struct pt_regs *regs) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(0, &saveset, regs)) - /* We will get here twice: once to call the signal - handler, then again to return from the - sigsuspend system call. When calling the - signal handler, R10 holds the signal number as - set through do_signal. The sigsuspend call - will return with the restored value set above; - always -EINTR. */ - return regs->r10; - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } int @@ -353,8 +301,8 @@ * user-mode trampoline. */ -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) +static int setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) { struct sigframe __user *frame; unsigned long return_ip; @@ -402,14 +350,15 @@ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs * regs) +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) { struct rt_sigframe __user *frame; unsigned long return_ip; @@ -466,21 +415,24 @@ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: force_sigsegv(sig, current); + return -EFAULT; } /* * OK, we're invoking a handler */ -static inline void +static inline int handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Are we from a system call? */ if (canrestart) { /* If so, check system call restarting.. */ @@ -510,19 +462,20 @@ /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + return ret; } /* @@ -537,12 +490,13 @@ * mode below. */ -int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +void do_signal(int canrestart, struct pt_regs *regs) { siginfo_t info; int signr; struct k_sigaction ka; - + sigset_t *oldset; + /* * We want the common case to go fast, which * is why we may in certain cases get here from @@ -550,16 +504,26 @@ * if so. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Whee! Actually deliver the signal. */ - handle_signal(canrestart, signr, &info, &ka, oldset, regs); - return 1; + if (handle_signal(canrestart, signr, &info, &ka, oldset, regs)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; } /* Did we come from a system call? */ @@ -575,5 +539,11 @@ regs->irp -= 2; } } - return 0; + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/time.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/time.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/time.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/time.c 2007-01-09 10:29:18.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.5 2004/09/29 06:12:46 starvik Exp $ +/* $Id: time.c,v 1.10 2007/01/09 09:29:18 starvik Exp $ * * linux/arch/cris/arch-v10/kernel/time.c * @@ -20,6 +20,7 @@ #include #include #include +#include /* define this if you need to use print_timestamp */ /* it will make jiffies at 96 hz instead of 100 hz though */ @@ -202,8 +203,9 @@ extern void cris_do_profile(struct pt_regs *regs); static inline irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_interrupt(int irq, void *dev_id) { + struct pt_regs* regs = get_irq_regs(); /* acknowledge the timer irq */ #ifdef USE_CASCADE_TIMERS @@ -222,9 +224,11 @@ #endif /* reset watchdog otherwise it resets us! */ - reset_watchdog(); + /* Update statistics. */ + update_process_times(user_mode(regs)); + /* call the real timer interrupt handler */ do_timer(1); diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/traps.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/traps.c --- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/traps.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/traps.c 2006-12-11 14:04:24.000000000 +0100 @@ -1,13 +1,10 @@ -/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $ +/* + * Helper functions for trap handlers * - * linux/arch/cris/arch-v10/traps.c + * Copyright (C) 2000-2006, Axis Communications AB. * - * Heler functions for trap handlers - * - * Copyright (C) 2000-2002 Axis Communications AB - * - * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Authors: Bjorn Wesen + * Hans-Peter Nilsson * */ @@ -15,124 +12,118 @@ #include #include -extern int raw_printk(const char *fmt, ...); - -void -show_registers(struct pt_regs * regs) +void +show_registers(struct pt_regs *regs) { - /* We either use rdusp() - the USP register, which might not - correspond to the current process for all cases we're called, - or we use the current->thread.usp, which is not up to date for - the current process. Experience shows we want the USP - register. */ + /* + * It's possible to use either the USP register or current->thread.usp. + * USP might not correspond to the current process for all cases this + * function is called, and current->thread.usp isn't up to date for the + * current process. Experience shows that using USP is the way to go. + */ unsigned long usp = rdusp(); - raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", - regs->irp, regs->srp, regs->dccr, usp, regs->mof ); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp, regs->mof); + + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs); - raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); - raw_printk("Process %s (pid: %d, stackpage=%08lx)\n", + + printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs); + + printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); + + printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, (unsigned long)current); /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (! user_mode(regs)) { - int i; + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + int i; - show_stack(NULL, (unsigned long*)usp); + show_stack(NULL, (unsigned long *)usp); - /* Dump kernel stack if the previous dump wasn't one. */ + /* + * If the previous stack-dump wasn't a kernel one, dump the + * kernel stack now. + */ if (usp != 0) - show_stack (NULL, NULL); + show_stack(NULL, NULL); - raw_printk("\nCode: "); - if(regs->irp < PAGE_OFFSET) - goto bad; - - /* Often enough the value at regs->irp does not point to - the interesting instruction, which is most often the - _previous_ instruction. So we dump at an offset large - enough that instruction decoding should be in sync at - the interesting point, but small enough to fit on a row - (sort of). We point out the regs->irp location in a - ksymoops-friendly way by wrapping the byte for that - address in parentheses. */ - for(i = -12; i < 12; i++) - { - unsigned char c; - if(__get_user(c, &((unsigned char*)regs->irp)[i])) { -bad: - raw_printk(" Bad IP value."); - break; - } + printk("\nCode: "); + + if (regs->irp < PAGE_OFFSET) + goto bad_value; + + /* + * Quite often the value at regs->irp doesn't point to the + * interesting instruction, which often is the previous + * instruction. So dump at an offset large enough that the + * instruction decoding should be in sync at the interesting + * point, but small enough to fit on a row. The regs->irp + * location is pointed out in a ksymoops-friendly way by + * wrapping the byte for that address in parenthesises. + */ + for (i = -12; i < 12; i++) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->irp)[i])) { +bad_value: + printk(" Bad IP value."); + break; + } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); - } - raw_printk("\n"); - } + printk("%02x ", c); + } + printk("\n"); + } } -/* Called from entry.S when the watchdog has bitten - * We print out something resembling an oops dump, and if - * we have the nice doggy development flag set, we halt here - * instead of rebooting. - */ - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - - void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); - while(1) /* nothing */; -#else - show_registers(regs); -#endif + asm volatile ("setf m"); } -/* This is normally the 'Oops' routine */ -void -die_if_kernel(const char * str, struct pt_regs * regs, long err) +extern void (*nmi_handler)(struct pt_regs*); +void handle_nmi(struct pt_regs* regs) { - if(user_mode(regs)) - return; - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* This printout might take too long and trigger the - * watchdog normally. If we're in the nice doggy - * development mode, stop the watchdog during printout. - */ - stop_watchdog(); -#endif - - raw_printk("%s: %04lx\n", str, err & 0xffff); - - show_registers(regs); + if (nmi_handler) + nmi_handler(regs); -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif - do_exit(SIGSEGV); + /* Wait until nmi is no longer active. (We enable NMI immediately after + returning from this function, and we don't want it happening while + exiting from the NMI interrupt handler.) */ + while(*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active)); } -void arch_enable_nmi(void) +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) { - asm volatile("setf m"); + struct bug_frame f; + unsigned char c; + unsigned long irp = regs->irp; + + if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f)) + return; + if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC) + return; + if (__get_user(c, f.filename)) + f.filename = ""; + + printk("kernel BUG at %s:%d!\n", f.filename, f.line); } +#endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksum.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksum.S --- linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksum.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksum.S 2005-08-16 12:38:52.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: checksum.S,v 1.2 2005/08/16 10:38:52 edgar Exp $ * A fast checksum routine using movem * Copyright (c) 1998-2001 Axis Communications AB * @@ -61,8 +61,6 @@ ax addq 0,$r12 - ax ; do it again, since we might have generated a carry - addq 0,$r12 subq 10*4,$r11 bge _mloop @@ -88,10 +86,6 @@ lsrq 16,$r13 ; r13 = checksum >> 16 and.d $r9,$r12 ; checksum = checksum & 0xffff add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 _no_fold: cmpq 2,$r11 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksumcopy.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksumcopy.S --- linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksumcopy.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksumcopy.S 2005-08-16 12:38:52.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: checksumcopy.S,v 1.2 2005/08/16 10:38:52 edgar Exp $ * A fast checksum+copy routine using movem * Copyright (c) 1998, 2001 Axis Communications AB * @@ -67,8 +67,6 @@ ax addq 0,$r13 - ax ; do it again, since we might have generated a carry - addq 0,$r13 subq 10*4,$r12 bge _mloop @@ -91,10 +89,6 @@ lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 _no_fold: cmpq 2,$r12 diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/dram_init.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/dram_init.S --- linux-2.6.19.2.old/arch/cris/arch-v10/lib/dram_init.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/dram_init.S 2006-10-13 14:43:11.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $ +/* $Id: dram_init.S,v 1.5 2006/10/13 12:43:11 starvik Exp $ * * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files @@ -11,6 +11,9 @@ * Authors: Mikael Starvik (starvik@axis.com) * * $Log: dram_init.S,v $ + * Revision 1.5 2006/10/13 12:43:11 starvik + * Merge of 2.6.18 + * * Revision 1.4 2003/09/22 09:21:59 starvik * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx * so we need to mask off 12 bits. diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/mm/fault.c linux-2.6.19.2.dev/arch/cris/arch-v10/mm/fault.c --- linux-2.6.19.2.old/arch/cris/arch-v10/mm/fault.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/mm/fault.c 2006-12-11 12:32:10.000000000 +0100 @@ -73,7 +73,7 @@ /* leave it to the MM system fault handler */ if (miss) do_page_fault(address, regs, 0, writeac); - else + else do_page_fault(address, regs, 1, we); /* Reload TLB with new entry to avoid an extra miss exception. @@ -84,12 +84,13 @@ local_irq_disable(); pmd = (pmd_t *)(pgd + pgd_index(address)); if (pmd_none(*pmd)) - return; + goto exit; pte = *pte_offset_kernel(pmd, address); if (!pte_present(pte)) - return; + goto exit; *R_TLB_SELECT = select; *R_TLB_HI = cause; *R_TLB_LO = pte_val(pte); +exit: local_irq_restore(flags); } diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/mm/tlb.c linux-2.6.19.2.dev/arch/cris/arch-v10/mm/tlb.c --- linux-2.6.19.2.old/arch/cris/arch-v10/mm/tlb.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v10/mm/tlb.c 2006-08-07 12:08:35.000000000 +0200 @@ -179,23 +179,26 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - /* make sure we have a context */ + if (prev != next) { + /* make sure we have a context */ + get_mmu_context(next); + + /* remember the pgd for the fault handlers + * this is similar to the pgd register in some other CPU's. + * we need our own copy of it because current and active_mm + * might be invalid at points where we still need to derefer + * the pgd. + */ - get_mmu_context(next); + per_cpu(current_pgd, smp_processor_id()) = next->pgd; - /* remember the pgd for the fault handlers - * this is similar to the pgd register in some other CPU's. - * we need our own copy of it because current and active_mm - * might be invalid at points where we still need to derefer - * the pgd. - */ - - per_cpu(current_pgd, smp_processor_id()) = next->pgd; - - /* switch context in the MMU */ + /* switch context in the MMU */ - D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + D(printk("switching mmu_context to %d (%p)\n", + next->context, next)); - *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context.page_id); + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, + page_id, next->context.page_id); + } } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v32/Kconfig --- linux-2.6.19.2.old/arch/cris/arch-v32/Kconfig 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/Kconfig 2007-01-09 10:29:18.000000000 +0100 @@ -1,23 +1,66 @@ +source drivers/cpufreq/Kconfig + config ETRAX_DRAM_VIRTUAL_BASE hex depends on ETRAX_ARCH_V32 default "c0000000" -config ETRAX_LED1G - string "First green LED bit" +choice + prompt "Nbr of Ethernet LED groups" depends on ETRAX_ARCH_V32 + default ETRAX_NBR_LED_GRP_ONE + help + Select how many Ethernet LED groups that can be used. Usually one per Ethernet + interface is a good choice. + +config ETRAX_NBR_LED_GRP_ZERO + bool "Use zero LED groups" + help + Select this if you do not want any Ethernet LEDs. + +config ETRAX_NBR_LED_GRP_ONE + bool "Use one LED group" + help + Select this if you want one Ethernet LED group. This LED group can be used for + one or more Ethernet interfaces. However, it is recomended that each Ethernet + interface use a dedicated LED group. + +config ETRAX_NBR_LED_GRP_TWO + bool "Use two LED groups" + help + Select this if you want two Ethernet LED groups. This is the best choice if you + have more than one Ethernet interface and would like to have separate LEDs for + the interfaces. + +endchoice + +config ETRAX_LED_G_NET0 + string "Ethernet LED group 0 green LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA3" help - Bit to use for the first green LED (network LED). - Most Axis products use bit A3 here. + Bit to use for the green LED in Ethernet LED group 0. -config ETRAX_LED1R - string "First red LED bit" - depends on ETRAX_ARCH_V32 +config ETRAX_LED_R_NET0 + string "Ethernet LED group 0 red LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA4" help - Bit to use for the first red LED (network LED). - Most Axis products use bit A4 here. + Bit to use for the red LED in Ethernet LED group 0. + +config ETRAX_LED_G_NET1 + string "Ethernet group 1 green LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the green LED in Ethernet LED group 1. + +config ETRAX_LED_R_NET1 + string "Ethernet group 1 red LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the red LED in Ethernet LED group 1. config ETRAX_LED2G string "Second green LED bit" @@ -294,3 +337,22 @@ help Configures the initial data for the general port E bits. Most products should use 00000 here. + +config ETRAX_DEF_GIO_PV_OE + hex "GIO_PV_OE" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the direction of virtual general port V bits. 1 is out, + 0 is in. This is often totally different depending on the product + used. These bits are used for all kinds of stuff. If you don't know + what to use, it is always safe to put all as inputs, although + floating inputs isn't good. + +config ETRAX_DEF_GIO_PV_OUT + hex "GIO_PV_OUT" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the initial data for the virtual general port V bits. + Most products should use 0000 here. diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/Makefile 2006-11-29 17:05:41.000000000 +0100 @@ -1,14 +1,21 @@ # # arch/cris/arch-v32/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary -R .note -R .comment -compressed/vmlinuz: $(objtree)/vmlinux - @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - rm -f zImage tools/build compressed/vmlinux.out - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(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 $< $@ + @echo ' Kernel: $@ is ready' diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/Makefile 2006-11-29 17:05:42.000000000 +0100 @@ -1,41 +1,29 @@ # -# lx25/arch/cris/arch-v32/boot/compressed/Makefile +# arch/cris/arch-v32/boot/compressed/Makefile # -# create a compressed vmlinux image from the original vmlinux files and romfs -# - -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -mlinux -march=v32 -I $(TOPDIR)/include CFLAGS = -O2 LD = gcc-cris -mlinux -march=v32 -nostdlib +LDFLAGS = -T $(obj)/decompress.ld +obj-y = head.o misc.o +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o - -# files to compress -SYSTEM = $(objtree)/vmlinux.bin - -all: vmlinuz - -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin -$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz - rm -f piggy.img - cp $(objtree)/vmlinuz $(src) +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $@ +targets := vmlinux piggy.gz decompress.o decompress.bin -# gzip the kernel image +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -piggy.img: $(SYSTEM) - cat $(SYSTEM) | gzip -f -9 > piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -clean: - rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS) +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/README linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/README --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/README 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/README 2003-08-21 11:37:03.000000000 +0200 @@ -5,14 +5,14 @@ This can be slightly confusing because it's a process with many steps. The kernel object built by the arch/etrax100/Makefile, vmlinux, is split -by that makefile into text and data binary files, vmlinux.text and +by that makefile into text and data binary files, vmlinux.text and vmlinux.data. Those files together with a ROM filesystem can be catted together and burned into a flash or executed directly at the DRAM origin. They can also be catted together and compressed with gzip, which is what -happens in this makefile. Together they make up piggy.img. +happens in this makefile. Together they make up piggy.img. The decompressor is built into the file decompress.o. It is turned into the binary file decompress.bin, which is catted together with piggy.img diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/decompress.ld linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/decompress.ld --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/decompress.ld 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/decompress.ld 2003-08-21 11:57:56.000000000 +0200 @@ -1,7 +1,7 @@ /*#OUTPUT_FORMAT(elf32-us-cris) */ OUTPUT_ARCH (crisv32) -MEMORY +MEMORY { dram : ORIGIN = 0x40700000, LENGTH = 0x00100000 diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/head.S --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/head.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/head.S 2007-01-09 10:29:20.000000000 +0100 @@ -2,19 +2,19 @@ * Code that sets up the DRAM registers, calls the * decompressor to unpack the piggybacked kernel, and jumps. * - * Copyright (C) 1999 - 2003, Axis Communications AB + * Copyright (C) 1999 - 2006, Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY #include #include #include - + #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 ;; Exported symbols - + .globl input_data .text @@ -29,53 +29,16 @@ REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 move.d $r0, [$r1] - ;; If booting from NAND flash we first have to copy some - ;; data from NAND flash to internal RAM to get the code - ;; that initializes the SDRAM. Lets copy 20 KB. This - ;; code executes at 0x38010000 if booting from NAND and - ;; we are guaranted that at least 0x200 bytes are good so - ;; lets start from there. The first 8192 bytes in the nand - ;; flash is spliced with zeroes and is thus 16384 bytes. - move.d 0x38010200, $r10 - move.d 0x14200, $r11 ; Start offset in NAND flash 0x10200 + 16384 - move.d 0x5000, $r12 ; Length of copy - - ;; Before this code the tools add a partitiontable so the PC - ;; has an offset from the linked address. -offset1: - lapcq ., $r13 ; get PC - add.d first_copy_complete-offset1, $r13 - -#include "../../lib/nand_init.S" - -first_copy_complete: - ;; Initialze the DRAM registers. + ;; Initialize the DRAM registers. cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? beq dram_init_finished nop #include "../../lib/dram_init.S" - + dram_init_finished: - lapcq ., $r13 ; get PC - add.d second_copy_complete-dram_init_finished, $r13 - - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne second_copy_complete ; No NAND boot - nop - - ;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM) - move.d 0x40204000, $r10 - move.d 0x8000, $r11 - move.d 0x200000, $r12 - ba copy_nand_to_ram - nop -second_copy_complete: - - ;; Initiate the PA port. + + ;; Initiate the GIO ports. move.d CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0 move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r1 move.d $r0, [$r1] @@ -84,57 +47,74 @@ move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r1 move.d $r0, [$r1] + move.d CONFIG_ETRAX_DEF_GIO_PB_OUT, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pb_dout), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PB_OE, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pb_oe), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PC_OUT, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pc_dout), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PC_OE, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pc_oe), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PD_OUT, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pd_dout), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PD_OE, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pd_oe), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PE_OUT, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pe_dout), $r1 + move.d $r0, [$r1] + + move.d CONFIG_ETRAX_DEF_GIO_PE_OE, $r0 + move.d REG_ADDR(gio, regi_gio, rw_pe_oe), $r1 + move.d $r0, [$r1] + ;; Setup the stack to a suitably high address. - ;; We assume 8 MB is the minimum DRAM and put + ;; We assume 8 MB is the minimum DRAM and put ;; the SP at the top for now. move.d 0x40800000, $sp - ;; Figure out where the compressed piggyback image is - ;; in the flash (since we wont try to copy it to DRAM - ;; before unpacking). It is at _edata, but in flash. + ;; Figure out where the compressed piggyback image is. + ;; It is either in [NOR] flash (we don't want to copy it + ;; to DRAM before unpacking), or copied to DRAM + ;; by the [NAND] flash boot loader. + ;; The piggyback image is at _edata, but relative to where the + ;; image is actually located in memory, not where it is linked + ;; (the decompressor is linked at 0x40700000+ and runs there). ;; Use (_edata - herami) as offset to the current PC. - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - beq hereami2 - nop -hereami: +hereami: lapcq ., $r5 ; get PC and.d 0x7fffffff, $r5 ; strip any non-cache bit - move.d $r5, $r0 ; save for later - flash address of 'herami' + move.d $r5, $r0 ; source address of 'herami' add.d _edata, $r5 sub.d hereami, $r5 ; r5 = flash address of '_edata' move.d hereami, $r1 ; destination - ba 2f - nop -hereami2: - lapcq ., $r5 ; get PC - and.d 0x00ffffff, $r5 ; strip any non-cache bit - move.d $r5, $r6 - or.d 0x40200000, $r6 - move.d $r6, $r0 ; save for later - flash address of 'herami' - add.d _edata, $r5 - sub.d hereami2, $r5 ; r5 = flash address of '_edata' - add.d 0x40200000, $r5 - move.d hereami2, $r1 ; destination -2: - ;; Copy text+data to DRAM + ;; Copy text+data to DRAM + move.d _edata, $r2 ; end destination -1: move.w [$r0+], $r3 - move.w $r3, [$r1+] - cmp.d $r2, $r1 +1: move.w [$r0+], $r3 ; from herami+ source + move.w $r3, [$r1+] ; to hereami+ destination (linked address) + cmp.d $r2, $r1 ; finish when destination == _edata bcs 1b nop - - move.d input_data, $r0 ; for the decompressor + move.d input_data, $r0 ; for the decompressor move.d $r5, [$r0] ; for the decompressor ;; Clear the decompressors BSS (between _edata and _end) - + moveq 0, $r0 move.d _edata, $r1 move.d _end, $r2 @@ -144,40 +124,47 @@ nop ;; Save command line magic and address. - move.d _cmd_line_magic, $r12 - move.d $r10, [$r12] - move.d _cmd_line_addr, $r12 - move.d $r11, [$r12] - + move.d _cmd_line_magic, $r0 + move.d $r10, [$r0] + move.d _cmd_line_addr, $r0 + move.d $r11, [$r0] + + ;; Save boot source indicator + move.d _boot_source, $r0 + move.d $r12, [$r0] + ;; Do the decompression and save compressed size in _inptr jsr decompress_kernel nop + ;; Restore boot source indicator + move.d _boot_source, $r12 + move.d [$r12], $r12 + ;; Restore command line magic and address. move.d _cmd_line_magic, $r10 move.d [$r10], $r10 move.d _cmd_line_addr, $r11 move.d [$r11], $r11 - + ;; Put start address of root partition in r9 so the kernel can use it ;; when mounting from flash move.d input_data, $r0 move.d [$r0], $r9 ; flash address of compressed kernel move.d inptr, $r0 add.d [$r0], $r9 ; size of compressed kernel - cmp.d 0x40200000, $r9 - blo enter_kernel - nop - sub.d 0x40200000, $r9 - add.d 0x4000, $r9 - -enter_kernel: + cmp.d 0x40000000, $r9 ; image in DRAM ? + blo enter_kernel ; no, must be [NOR] flash, jump + nop ; delay slot + and.d 0x001fffff, $r9 ; assume compressed kernel was < 2M + +enter_kernel: ;; Enter the decompressed kernel move.d RAM_INIT_MAGIC, $r8 ; Tell kernel that DRAM is initialized jump 0x40004000 ; kernel is linked to this address nop - + .data input_data: @@ -185,8 +172,8 @@ _cmd_line_magic: .dword 0 _cmd_line_addr: + .dword 0 +_boot_source: .dword 0 -is_nand_boot: - .dword 0 - + #include "../../lib/hw_settings.S" diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/misc.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/misc.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/misc.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/misc.c 2006-11-03 11:35:51.000000000 +0100 @@ -1,15 +1,15 @@ /* * misc.c * - * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $ - * - * This is a collection of several routines from gzip-1.0.3 + * $Id: misc.c,v 1.12 2006/11/03 10:35:51 pkj Exp $ + * + * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 * puts by Nick Holloway 1993, better puts by Martin Mares 1995 * adoptation for Linux/CRIS Axis Communications AB, 1999 - * + * */ /* where the piggybacked kernel image expects itself to live. @@ -20,11 +20,11 @@ #define KERNEL_LOAD_ADR 0x40004000 - #include #include #include #include +#include /* * gzip declarations @@ -66,8 +66,8 @@ #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ #define RESERVED 0xC0 /* bit 6,7: reserved */ -#define get_byte() inbuf[inptr++] - +#define get_byte() inbuf[inptr++] + /* Diagnostic functions */ #ifdef DEBUG # define Assert(cond,msg) {if(!(cond)) error(msg);} @@ -96,20 +96,20 @@ static long bytes_out = 0; static uch *output_data; static unsigned long output_ptr = 0; - + static void *malloc(int size); static void free(void *where); static void error(char *m); static void gzip_mark(void **); static void gzip_release(void **); - + static void puts(const char *); /* the "heap" is put directly after the BSS ends, at end */ - + extern int _end; static long free_mem_ptr = (long)&_end; - + #include "../../../../../lib/inflate.c" static void *malloc(int size) @@ -152,7 +152,7 @@ rs = REG_RD(ser, regi_ser, rs_stat_din); } while (!rs.tr_rdy);/* Wait for tranceiver. */ - + REG_WR(ser, regi_ser, rw_dout, dout); } @@ -209,9 +209,9 @@ ulg c = crc; /* temporary variable */ unsigned n; uch *in, *out, ch; - + in = window; - out = &output_data[output_ptr]; + out = &output_data[output_ptr]; for (n = 0; n < outcnt; n++) { ch = *out++ = *in++; c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); @@ -225,9 +225,9 @@ static void error(char *x) { - puts("\n\n"); + puts("\r\n\n"); puts(x); - puts("\n\n -- System halted\n"); + puts("\r\n\n -- System halted\n"); while(1); /* Halt */ } @@ -246,13 +246,13 @@ reg_ser_rw_rec_ctrl rec_ctrl; reg_ser_rw_tr_baud_div tr_baud; reg_ser_rw_rec_baud_div rec_baud; - + /* Turn off XOFF. */ xoff = REG_RD(ser, regi_ser, rw_xoff); - + xoff.chr = 0; xoff.automatic = regk_ser_no; - + REG_WR(ser, regi_ser, rw_xoff, xoff); /* Set baudrate and stopbits. */ @@ -260,19 +260,21 @@ rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl); tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div); rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div); - + tr_ctrl.stop_bits = 1; /* 2 stop bits. */ - - /* - * The baudrate setup is a bit fishy, but in the end the tranceiver is - * set to 4800 and the receiver to 115200. The magic value is - * 29.493 MHz. + tr_ctrl.en = 1; /* enable transmitter */ + rec_ctrl.en = 1; /* enabler receiver */ + + /* + * The baudrate setup used to be a bit fishy, but now transmitter and + * receiver are both set to the intended baud rate, 115200. + * The magic value is 29.493 MHz. */ tr_ctrl.base_freq = regk_ser_f29_493; rec_ctrl.base_freq = regk_ser_f29_493; - tr_baud.div = (29493000 / 8) / 4800; + tr_baud.div = (29493000 / 8) / 115200; rec_baud.div = (29493000 / 8) / 115200; - + REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud); REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl); @@ -283,22 +285,28 @@ decompress_kernel() { char revision; - + reg_pinmux_rw_hwprot hwprot; + /* input_data is set in head.S */ inbuf = input_data; - + + hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); #ifdef CONFIG_ETRAX_DEBUG_PORT0 serial_setup(regi_ser0); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT1 + hwprot.ser1 = regk_pinmux_yes; serial_setup(regi_ser1); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT2 + hwprot.ser2 = regk_pinmux_yes; serial_setup(regi_ser2); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT3 + hwprot.ser3 = regk_pinmux_yes; serial_setup(regi_ser3); #endif + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); setup_normal_output_buffer(); @@ -307,11 +315,11 @@ __asm__ volatile ("move $vr,%0" : "=rm" (revision)); if (revision < 32) { - puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n"); + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n"); while(1); } - puts("Uncompressing Linux...\n"); + puts("Uncompressing Linux...\r\n"); gunzip(); - puts("Done. Now booting the kernel.\n"); + puts("Done. Now booting the kernel.\r\n"); } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/Makefile 2007-01-17 14:24:50.000000000 +0100 @@ -1,36 +1,29 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) CFLAGS = -O2 -LD = gcc-cris -mlinux -march=v32 -nostdlib +LD = gcc-cris -mlinux -march=v32 -nostdlib +LDFLAGS = -T $(obj)/rescue.ld +LDPOSTFLAGS = -lgcc OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss - -all: $(target)/rescue.bin - -rescue: rescue.bin - # do nothing - -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin - cp -p $(target)/rescue.bin $(objtree) - -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: +obj-y = head.o bootload.o crisv32_nand.o nand_base.o nand_ids.o nand_ecc.o \ + lib.o +OBJECTS = $(obj)/head.o $(obj)/bootload.o \ + $(obj)/crisv32_nand.o $(obj)/nand_base.o \ + $(obj)/nand_ids.o $(obj)/nand_ecc.o \ + $(obj)/lib.o $(obj)/../../lib/lib.a + +targets := rescue.o rescue.bin + +quiet_cmd_ldlibgcc = LD $@ +cmd_ldlibgcc = $(LD) $(LDFLAGS) $(filter-out FORCE,$^) $(LDPOSTFLAGS) -o $@ + +$(obj)/rescue.o: $(OBJECTS) FORCE + $(call if_changed,ldlibgcc) + +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/bootload.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/bootload.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/bootload.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/bootload.c 2006-11-28 11:05:39.000000000 +0100 @@ -0,0 +1,277 @@ +/* + * bootload.c + * Simple boot loader for NAND chips on Etrax FS + * + * $Id: bootload.c,v 1.8 2006/11/28 10:05:39 ricardw Exp $ + * + */ + +#include +#include + +#include "mtd.h" +#include "nand.h" + +#include +#include +#include + +#include "lib.h" + +#define BOOT_ADDR (CONFIG_ETRAX_PTABLE_SECTOR + 0x40200000) + +/* bits for nand_rw() `cmd'; or together as needed */ + +#define NANDRW_READ 0x01 +#define NANDRW_WRITE 0x00 +#define NANDRW_JFFS2 0x02 +#define NANDRW_JFFS2_SKIP 0x04 + +#define ROUND_DOWN(value, boundary) ((value) & (~((boundary)-1))) + +/* set $r8 to RAM_INIT_MAGIC, $r12 to NAND_BOOT_MAGIC then jump */ +#define BOOT(addr) __asm__ volatile (" \ + move.d 0x56902387, $r8\n\ + move.d 0x9A9DB001, $r12\n\ + jump %0\n\ + nop\n\ + " : : "r" (addr)) + +#define D(x) + +extern struct mtd_info *crisv32_nand_flash_probe(void); + +extern int _end, _bss, _edata; + +/* + * NAND read/write from U-Boot 1.4.4 + * Modified for newer mtd and use of mtd interface instead of nand directly. + * + * cmd: 0: NANDRW_WRITE write, fail on bad block + * 1: NANDRW_READ read, fail on bad block + * 2: NANDRW_WRITE | NANDRW_JFFS2 write, skip bad blocks + * 3: NANDRW_READ | NANDRW_JFFS2 read, data all 0xff for bad blocks + * 7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks + */ +static int +nand_rw (struct mtd_info* mtd, int cmd, + size_t start, size_t len, + size_t *retlen, u_char * buf) +{ + int ret = 0, n, total = 0; + + /* eblk (once set) is the start of the erase block containing the + * data being processed. + */ + size_t eblk = ~0; /* force mismatch on first pass */ + size_t erasesize = mtd->erasesize; + + while (len) { + if ((start & (-erasesize)) != eblk) { + /* have crossed into new erase block, deal with + * it if it is marked bad. + */ + eblk = start & (-erasesize); /* start of block */ + D( + puts("New block "); + putx(eblk); + putnl(); + ) + if (mtd->block_isbad(mtd, eblk)) { + if (cmd == (NANDRW_READ | NANDRW_JFFS2)) { + while (len > 0 && + start - eblk < erasesize) { + *(buf++) = 0xff; + ++start; + ++total; + --len; + } + continue; + } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) { + start += erasesize; + continue; + } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) { + /* skip bad block */ + start += erasesize; + continue; + } else { + ret = 1; + break; + } + } + } + /* The ECC will not be calculated correctly if + less than 512 is written or read */ + /* Is request at least 512 bytes AND it starts on a proper boundry */ + if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200)) + puts("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\r\n"); + +#if 0 + buf = (void *) 0x38008000; /* to fixed address, for testing */ +#endif + + if (cmd & NANDRW_READ) { + ret = mtd->read_ecc(mtd, start, + min(len, eblk + erasesize - start), + (size_t *)&n, (u_char*)buf, + NULL, NULL); + } else { + ret = mtd->write_ecc(mtd, start, + min(len, eblk + erasesize - start), + (size_t *)&n, (u_char*)buf, + NULL, NULL); + } + + if (ret) { + break; + } + + start += n; + buf += n; + total += n; + len -= n; + } + if (retlen) + *retlen = total; + + return ret; +} + + +void +bootload() +{ + char revision; + struct mtd_info *mtd; + + serial_init(); + + __asm__ volatile ("move $vr,%0" : "=rm" (revision)); + if (revision < 32) + { + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n"); + while(1); + } + + puts("\r\n\nETRAX FS NAND boot loader\r\n"); + puts("=========================\r\n"); + puts("Rev 1, " __DATE__ " " __TIME__ "\r\n"); + + puts("CPU revision: "); + putx(revision); + putnl(); + + puts("Bootloader main at ") ; + putx((int) bootload); + putnl(); + + puts("Data end: "); + putx((long) &_edata); + putnl(); + + puts("Bss: "); + putx((long) &_bss); + putnl(); + + puts("Heap: "); + putx((long) &_end); + putnl(); + +#if 0 /* loop calibration */ + volatile int i; + puts ("10000 loops..."); + for (i = 0; i < 10000; i++) + udelay(1000); + puts("done\r\n"); +#endif + + puts("Identifying nand chip...\r\n"); + mtd = crisv32_nand_flash_probe(); + puts("Done.\r\n"); + + if (mtd) { + puts("Chip identified. "); +#if 0 /* print chip parameters */ + if (mtd->name) + puts(mtd->name); + + puts("\r\ntype: "); + putx(mtd->type); + puts("\r\nflags: "); + putx(mtd->flags); + puts("\r\nsize: "); + putx(mtd->size); + puts("\r\nerasesize: "); + putx(mtd->erasesize); + puts("\r\noobblock: "); + putx(mtd->oobblock); + puts("\r\noobsize: "); + putx(mtd->oobsize); + puts("\r\necctype: "); + putx(mtd->ecctype); + puts("\r\neccsize: "); + putx(mtd->eccsize); +#endif + putnl(); + + puts("Bad blocks:\r\n"); + + int i; + for (i = 0; i < mtd->size; i += mtd->erasesize) { + if (mtd->block_isbad(mtd, i)) { + putx(i); + putnl(); + } + } + +#if 0 /* print oob parameters */ + puts("Oob info:\r\n"); + puts("useecc: "); + putx(mtd->oobinfo.useecc); + puts("\r\neccbytes: "); + putx(mtd->oobinfo.eccbytes); + puts("\r\neccpos: "); + for (i = 0; i < mtd->oobinfo.eccbytes; i++) { + putx(mtd->oobinfo.eccpos[i]); + putc(' '); + } + putnl(); +#endif + + puts("Bootload in progress..."); + int res, copied; + res = nand_rw(mtd, + NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP, + CONFIG_ETRAX_PTABLE_SECTOR, + 0x200000, /* 2 megs */ + &copied, + (void *) BOOT_ADDR); + + puts("complete, status "); + putx(res); + puts(", loaded "); + putx(copied); + puts(" bytes\r\n"); +#if 1 + puts("Data in DRAM:\r\n"); + putx(* (int *) (BOOT_ADDR + 0)); + putc(' '); + putx(* (int *) (BOOT_ADDR + 4)); + putc(' '); + putx(* (int *) (BOOT_ADDR + 8)); + putnl(); +#endif + + if (res) + error("Corrupt data in NAND flash."); + else + { + puts("Booting...\r\n"); + BOOT(BOOT_ADDR); + } + } else + error("No NAND flash chip found to boot from."); + + while (1) + ; /* hang around until hell freezes over */ +} diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/crisv32_nand.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/crisv32_nand.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/crisv32_nand.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/crisv32_nand.c 2006-11-21 15:40:02.000000000 +0100 @@ -0,0 +1,157 @@ +/* + * Taken from arch/cris/arch-v32/drivers/nandflash.c + * and modified to use for boot loader. + * + * Copyright (c) 2004 + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: crisv32_nand.c,v 1.2 2006/11/21 14:40:02 ricardw Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "mtd.h" +#include "nand.h" + +#if 0 +#include +#endif + +#if 0 +#include +#endif + +#include +#include +#include +#include + +#include "lib.h" + +/* Hardware */ + +#define CE_BIT 4 +#define CLE_BIT 5 +#define ALE_BIT 6 +#define BY_BIT 7 + +#define NAND_RD_ADDR 0x90000000 /* read address */ +#define NAND_WR_ADDR 0x94000000 /* write address */ + +static struct mtd_info *crisv32_mtd = NULL; + +/* + * hardware specific access to control-lines + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) +{ + unsigned long flags; + reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + + switch(cmd){ + case NAND_CTL_SETCLE: + dout.data |= (1<> BY_BIT); +} + +/* + * Main initialization routine + */ +struct mtd_info* crisv32_nand_flash_probe (void) +{ + reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg); + reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe); + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + crisv32_mtd = malloc (sizeof(struct mtd_info) + sizeof (struct nand_chip)); + if (!crisv32_mtd) { + puts ("Unable to allocate CRISv32 NAND MTD device structure.\r\n"); + err = -ENOMEM; + return NULL; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&crisv32_mtd[1]); + + pa_oe.oe |= 1 << CE_BIT; + pa_oe.oe |= 1 << ALE_BIT; + pa_oe.oe |= 1 << CLE_BIT; + pa_oe.oe &= ~ (1 << BY_BIT); + REG_WR(gio, regi_gio, rw_pa_oe, pa_oe); + + bif_cfg.gated_csp0 = regk_bif_core_rd; + bif_cfg.gated_csp1 = regk_bif_core_wr; + REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg); + + /* Initialize structures */ + memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + crisv32_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = (void *) NAND_RD_ADDR; + this->IO_ADDR_W = (void *) NAND_WR_ADDR; + this->hwcontrol = crisv32_hwcontrol; + this->dev_ready = crisv32_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->eccmode = NAND_ECC_SOFT; + +#if 0 /* don't use BBT in boot loader */ + /* Enable the following for a flash based bad block table */ + this->options = NAND_USE_FLASH_BBT; +#endif + /* don't scan for BBT */ + this->options = NAND_SKIP_BBTSCAN; + + /* Scan to find existance of the device */ + if (nand_scan (crisv32_mtd, 1)) { + err = -ENXIO; + puts ("nand_scan failed\r\n"); + free (crisv32_mtd); + return NULL; + } + + return crisv32_mtd; +} diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/head.S --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/head.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/head.S 2007-01-31 16:52:19.000000000 +0100 @@ -1,14 +1,85 @@ -/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $ - * - * This used to be the rescue code but now that is handled by the - * RedBoot based RFL instead. Nothing to see here, move along. +/* $Id: head.S,v 1.16 2007/01/31 15:52:19 pkj Exp $ + * + * Simple boot loader which can also handle NAND chips. */ -#include -#include +#include +#include + +#define RAM_INIT_MAGIC 0x56902387 +#define NAND_BOOT_MAGIC 0x9A9DB001 + +;; Debugging. Normally all these are set to 0. +#define LEDS (0) +#define LEDTEST (0) +#define FLASH_LEDS_INSTEAD_OF_BOOT (0) +#define SERIAL_DUMP (0) +#define SERIAL_PORT (0) +#define SERIAL_RECEIVE (0) + +#if LEDS +#include +#include +#include +#endif + +#if SERIAL_DUMP +#include +#include +#endif + + +#if (SERIAL_PORT == 0) +#define regi_serial regi_ser0 +#endif +#if (SERIAL_PORT == 1) +#define regi_serial regi_ser1 +#endif + +;; Macros + +#if LEDS +.macro SAY x + orq 31, $r9 + and.d ~(\x), $r9 + move.d $r9, [$r8] +.endm +#else +.macro SAY x + ;; nothing +.endm +#endif + +#if SERIAL_DUMP +.macro DISPLAY x + move.d \x, $r6 ; save value + moveq 28, $r5 ; counter / shift amount +8: + move.d $r6, $r3 ; fetch value + bsr nybbleout + lsr.d $r5, $r3 ; shift nybble we want (delay slot) + subq 4, $r5 ; count down bits + bpl 8b ; loop + nop + bsr serout + moveq 13, $r3 ; delay slot + bsr serout + moveq 10, $r3 ; delay slot +.endm +#else +.macro DISPLAY x + ;; nothing +.endm +#endif + + +;; Code .text +start: + ;; TODO: Add code for Ronny's search-for-good-block boot rom algorithm + ;; Start clocks for used blocks. move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 move.d [$r1], $r0 @@ -17,22 +88,258 @@ REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 move.d $r0, [$r1] - ;; Copy 68KB NAND flash to Internal RAM (if NAND boot) - move.d 0x38004000, $r10 - move.d 0x8000, $r11 - move.d 0x11000, $r12 - move.d copy_complete, $r13 - and.d 0x000fffff, $r13 - or.d 0x38000000, $r13 + +#if LEDS + ;; set up for led control on PB 0..4 + move.d REG_ADDR(gio, regi_gio, rw_pb_dout), $r8 ; output reg + move.d [$r8], $r9 ; shadow + + ;; set up pinmux + move.d REG_ADDR(pinmux, regi_pinmux, rw_pb_gio), $r2 + move.d [$r2], $r3 + orq 31, $r3 + move.d $r3, [$r2] + + ;; set up GPIO + ;; set to outputs + move.d REG_ADDR(gio, regi_gio, rw_pb_oe), $r2 + move.d [$r2], $r3 + orq 31, $r3 + move.d $r3, [$r2] + + +#if LEDTEST + ;; led test + + moveq 0, $r1 ; led 5-bit binary counter +1: + or.d 31, $r9 + xor $r1, $r9 + move.d $r9, [$r8] + addq 1, $r1 + + move.d 100000000, $r2 ; delay loop (100e6 => 1s @200Mc) +2: + bne 2b + subq 1, $r2 + + ba 1b ; loop + nop +#endif + +#endif + + +check_nand_boot: + ;; Check boot source by checking highest nybble of PC: + ;; If we're running at address 0x0XXXXXXX, we're in flash/eprom/sram + ;; If we're running at address 0x38000000, we're in internal RAM, + ;; so we're most likely coming from NAND. + ;; If we're running at address 0x40000000, we're in SDRAM, + ;; so we've most likely been started by some sort of bootstrapper + ;; e.g. fsboot, which in turn implies NAND, else we would be booting + ;; normally at 0x0XXXXXXX + + SAY 1 + +here: + lapcq ., $r0 ; get PC + sub.d (here-start), $r0 ; offset from here + beq normal_boot ; running at address 0 - normal boot + move.d $r0, $r13 ; save offset for later (unused delay slot) + lsrq 28, $r0 ; get highest nybble + + SAY 2 + + ;; Prepare to copy 128KB of the NAND flash to internal RAM + move.d 0x38000200, $r10 ; dest in internal RAM + move.d 0x1fe00, $r12 ; #bytes + cmpq 4, $r0 ; running in RAM => started by fsboot + bhs copy_from_ram + movu.w 0x0200, $r11 ; source in flash (byte address) (DELAY SLOT) #include "../../lib/nand_init.S" - ;; No NAND found +#if LEDS + ;; must set up registers again (clobbered by nand_init) + move.d REG_ADDR(gio, regi_gio, rw_pb_dout), $r8 ; output reg + move.d [$r8], $r9 ; shadow +#endif + + SAY 3 + + ba copy_complete + nop + + ; Since the code above has to reside within the first 256 bytes of + ; NAND flash, verify that the code so far hasn't gone past this + ; limit. If you're considering removing this, you haven't + ; properly understood the problem; see nand_init.S for details. + .org PAGE_SIZE_ADDRESSES ; from nand_init.S + .org PAGE_SIZE_BYTES ; from nand_init.S + +normal_boot: + SAY 4 + + ;; Normal NOR boot move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser + jump $r10 ; Jump to decompresser + nop + +copy_from_ram: + add.d $r13, $r11 ; mem offs + src offs -> src addr +1: + move.d [$r11+], $r0 ; read source + subq 4, $r12 ; 4 bytes at a time + bne 1b ; loop until done + move.d $r0, [$r10+] ; write dest (DELAY SLOT) + jump copy_complete ; jump to internal RAM + nop ; delay slot + +copy_complete: + SAY 5 + +#if FLASH_LEDS_INSTEAD_OF_BOOT + +flash: + + ;; led test + + moveq 0, $r1 ; led binary counter +1: + or.d 31, $r9 + xor $r1, $r9 + move.d $r9, [$r8] + addq 1, $r1 + + move.d 100000000, $r2 ; delay loop (100e6 => 1s) +2: + bne 2b + subq 1, $r2 + + ba 1b ; loop forever nop +#endif -copy_complete: - move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser + +#if SERIAL_DUMP + ;; dump memory to serial port + +#if (SERIAL_PORT == 1) + ;; set up serial-1 pins + move.d REG_ADDR(pinmux, regi_pinmux, rw_hwprot), $r1 + move.d [$r1], $r0 + or.d REG_STATE(pinmux, rw_hwprot, ser1, yes), $r0 + move.d $r0, [$r1] +#endif + + ;; rw_xoff: chr and automatic = 0 + move.d REG_ADDR(ser, regi_serial, rw_xoff), $r1 + move.d [$r1], $r0 + and.d ~(REG_MASK(ser, rw_xoff, automatic) | REG_MASK(ser, rw_xoff, chr)), $r0 + move.d $r0, [$r1] + + ;; tr control + move.d REG_ADDR(ser, regi_serial, rw_tr_ctrl), $r1 + move.d [$r1], $r0 + and.d ~(REG_MASK(ser, rw_tr_ctrl, base_freq) | REG_MASK(ser, rw_tr_ctrl, stop_bits)), $r0 + or.d REG_STATE(ser, rw_tr_ctrl, stop_bits, bits2) | REG_STATE(ser, rw_tr_ctrl, base_freq, f29_493) | REG_STATE(ser, rw_tr_ctrl, en, yes), $r0 + move.d $r0, [$r1] + + ;; tr baud + move.d REG_ADDR(ser, regi_serial, rw_tr_baud_div), $r1 + move.d [$r1], $r0 + move.w (29493000 / 8) / 115200, $r0 + move.d $r0, [$r1] + +#if SERIAL_RECEIVE + ;; rec control + move.d REG_ADDR(ser, regi_serial, rw_rec_ctrl), $r1 + move.d [$r1], $r0 + and.d ~(REG_MASK(ser, rw_rec_ctrl, base_freq)), $r0 + or.d REG_STATE(ser, rw_rec_ctrl, base_freq, f29_493) | REG_STATE(ser, rw_tr_ctrl, en, yes), $r0 + move.d $r0, [$r1] + + ;; rec baud + move.d REG_ADDR(ser, regi_serial, rw_rec_baud_div), $r1 + move.d [$r1], $r0 + move.w (29493000 / 8) / 115200, $r0 + move.d $r0, [$r1] +#endif + + ;; dump memory + + move.d 0x38000000, $r5 ; pointer + + bsr serout + moveq 13, $r3 + bsr serout + moveq 10, $r3 + bsr serout + moveq 10, $r3 + +1: + movu.b [$r5+], $r3 ; get value + move.d $r3, $r6 ; save + + bsr nybbleout + lsrq 4, $r3 ; high nybble (delay slot) + + move.d $r6, $r3 ; restore + bsr nybbleout + andq 15, $r3 ; delay slot + + movu.b 32, $r3 + bsr serout nop + + move.d $r5, $r3 + andq 15, $r3 + bne 1b + nop + + bsr serout + moveq 13, $r3 ; delay slot + bsr serout + moveq 10, $r3 ; delay slot + + ba 1b ; loop forever + nop + +nybbleout: + cmpq 10, $r3 + blo 1f + addq 48, $r3 ; delay slot + addq 7, $r3 +1: +serout: + move.d REG_ADDR(ser, regi_serial, rs_stat_din), $r1 + move.d REG_ADDR(ser, regi_serial, rw_dout), $r2 +2: + move.d [$r1], $r4 + btstq REG_BIT(ser, rs_stat_din, tr_rdy), $r4 + bpl 2b + nop + ret + move.d $r3, [$r2] ; delay slot + +#endif + +;; Init DRAM + +#include "../../lib/dram_init.S" + move.d RAM_INIT_MAGIC, $r8 ; tell kernel boot loader dram init'd + move.d NAND_BOOT_MAGIC, $r12 ; booted from NAND flash + +;; TODO: Clear .bss (not needed yet because size of .bss is zero) +;; TODO: Change .ld script so that BSS is in DRAM? + +;; Ok, time to do something. Continue with boot loader in C. +;; Must set up minimal C environment first though. + + move.d 0x38020000, $sp ; stack pointer at top of internal RAM + + move.d bootload, $r10 + jump $r10 ; Jump to boot loader + nop + diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.c 2006-11-03 11:35:52.000000000 +0100 @@ -0,0 +1,243 @@ +/* + * lib.c + * Small practical functions for boot loader + * malloc/free + * memcpy/memset + * writeb/writew/readb/readw + * putc/puts/putnybble/putx + * error/error2/BUG + * serial_init + * + * $Id: lib.c,v 1.4 2006/11/03 10:35:52 pkj Exp $ + * + */ + +#include +#include +#include +#include +#include + +#include "lib.h" + +/* the "heap" is put directly after BSS ends, at _end */ + +extern int _end; +static long free_mem_ptr = (long)&_end; + +void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + return p; +} + +void free(void *where) +{ /* Don't care */ +} + +/* I/O */ + +unsigned char readb(const volatile void *addr) +{ + return *(volatile unsigned char *) addr; +} + +unsigned short readw(const volatile void *addr) +{ + return *(volatile unsigned short *) addr; +} + +void writeb(unsigned char b, volatile void *addr) +{ + *(volatile unsigned char *) addr = b; +} + +void writew(unsigned short b, volatile void *addr) +{ + *(volatile unsigned short *) addr = b; +} + +/* info and error messages to serial console */ + +#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL +static inline void +serout(const char c, reg_scope_instances regi_ser) +{ + reg_ser_rs_stat_din rs; + reg_ser_rw_dout dout = {.data = c}; + + do { + rs = REG_RD(ser, regi_ser, rs_stat_din); + } + while (!rs.tr_rdy);/* Wait for tranceiver. */ + + REG_WR(ser, regi_ser, rw_dout, dout); +} + + +void +putc(const char c) +{ +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + serout(c, regi_ser0); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT1 + serout(c, regi_ser1); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT2 + serout(c, regi_ser2); +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT3 + serout(c, regi_ser3); +#endif +} + + +void +puts(const char *s) +{ + while (*s) + putc(*s++); +} + +void +putnybble(int n) +{ + putc("0123456789abcdef"[n & 15]); +} + +void +putx(int x) +{ + int i; + + puts("0x"); + for (i = 7; i >= 0; i--) + putnybble(x >> 4*i); +} + +void +putnl() +{ + puts("\r\n"); +} + +#endif /* CONFIG_ETRAX_DEBUG_PORT_NULL */ + +void* +memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i + +/* nice stuff we need without having any library around */ + +void* memset(void* s, int c, size_t n); +void* memcpy(void* __dest, __const void* __src, + size_t __n); + +#define memzero(s, n) memset ((s), 0, (n)) + +#undef BUG +#define BUG() error2("BUG in " __FILE__, __LINE__, __FUNCTION__) + +void *malloc(int size); +void free(void *where); +void error(const char *m); +void error2(const char *m, int l, const char *f); +void serial_init(void); + +#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL +void putc(const char); +void puts(const char *); +void putnybble(int n); +void putx(int x); +void putnl(); +#else +#define putc(ch) +#define puts(str) +#define putnybble(nyb) +#define putx(x) +#define putnl() +#endif /* CONFIG_ETRAX_DEBUG_PORT_NULL */ + +unsigned char readb(const volatile void *addr); +unsigned short readw(const volatile void *addr); +void writeb(unsigned char b, volatile void *addr); +void writew(unsigned short b, volatile void *addr); + +#endif /* _LIB_H */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd-abi.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd-abi.h --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd-abi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd-abi.h 2006-09-06 11:21:07.000000000 +0200 @@ -0,0 +1,121 @@ +/* + * $Id: mtd-abi.h,v 1.1 2006/09/06 09:21:07 ricardw Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ + +#ifndef __MTD_ABI_H__ +#define __MTD_ABI_H__ + +#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ +#define __user +#endif + +struct erase_info_user { + uint32_t start; + uint32_t length; +}; + +struct mtd_oob_buf { + uint32_t start; + uint32_t length; + unsigned char __user *ptr; +}; + +#define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 +#define MTD_NORFLASH 3 +#define MTD_NANDFLASH 4 +#define MTD_PEROM 5 +#define MTD_DATAFLASH 6 +#define MTD_OTHER 14 +#define MTD_UNKNOWN 15 + +#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) +#define MTD_SET_BITS 2 // Bits can be set +#define MTD_ERASEABLE 4 // Has an erase function +#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible +#define MTD_VOLATILE 16 // Set for RAMs +#define MTD_XIP 32 // eXecute-In-Place possible +#define MTD_OOB 64 // Out-of-band data (NAND flash) +#define MTD_ECC 128 // Device capable of automatic ECC +#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed +#define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions + +// Some common devices / combinations of capabilities +#define MTD_CAP_ROM 0 +#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) +#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) +#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) +#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) + + +// Types of automatic ECC/Checksum available +#define MTD_ECC_NONE 0 // No automatic ECC available +#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip +#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices + +/* ECC byte placement */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default + +/* OTP mode selection */ +#define MTD_OTP_OFF 0 +#define MTD_OTP_FACTORY 1 +#define MTD_OTP_USER 2 + +struct mtd_info_user { + uint8_t type; + uint32_t flags; + uint32_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t oobblock; // Size of OOB blocks (e.g. 512) + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + uint32_t ecctype; + uint32_t eccsize; +}; + +struct region_info_user { + uint32_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ + uint32_t regionindex; +}; + +struct otp_info { + uint32_t start; + uint32_t length; + uint32_t locked; +}; + +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) +#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +#define MEMSETBADBLOCK _IOW('M', 12, loff_t) +#define OTPSELECT _IOR('M', 13, int) +#define OTPGETREGIONCOUNT _IOW('M', 14, int) +#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) +#define OTPLOCK _IOR('M', 16, struct otp_info) + +struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; + uint32_t eccpos[32]; +}; + +#endif /* __MTD_ABI_H__ */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd.h --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd.h 2006-12-14 07:59:24.000000000 +0100 @@ -0,0 +1,270 @@ +/* + * $Id: mtd.h,v 1.5 2006/12/14 06:59:24 ricardw Exp $ + * + * Copyright (C) 1999-2003 David Woodhouse et al. + * + * Released under GPL + */ + +#ifndef __MTD_MTD_H__ +#define __MTD_MTD_H__ + +#ifndef __KERNEL__ +#error This is a kernel header. Perhaps include mtd-user.h instead? +#endif + +/* only include absolutely necessary linux headers which will not change + * significantly + */ +#include +#include + +#if 0 +#include +#include +#include + +#include +#include +#endif + +#include "mtd-abi.h" + +/* local config, we don't want linux/config.h here */ +/* any undef/define pairs here avoid warnings due to linux autoconf includes */ +#undef CONFIG_MTD_PARTITIONS +#undef CONFIG_MTD_DEBUG +#undef CONFIG_MTD_NAND_VERIFY_WRITE +#define CONFIG_MTD_NAND_VERIFY_WRITE + +/* our MTD config */ + +#define NAND_BBT_SUPPORT 0 +#define NAND_WRITE_SUPPORT 0 +#define NAND_ERASE_SUPPORT 0 +#define NAND_MULTICHIP_SUPPORT 0 +#define NAND_HWECC_SUPPORT 0 +#define NAND_KVEC_SUPPORT 0 + + +#define MTD_CHAR_MAJOR 90 +#define MTD_BLOCK_MAJOR 31 +#define MAX_MTD_DEVICES 16 + +#define MTD_ERASE_PENDING 0x01 +#define MTD_ERASING 0x02 +#define MTD_ERASE_SUSPEND 0x04 +#define MTD_ERASE_DONE 0x08 +#define MTD_ERASE_FAILED 0x10 + +/* If the erase fails, fail_addr might indicate exactly which block failed. If + fail_addr = 0xffffffff, the failure was not at the device level or was not + specific to any particular block. */ +struct erase_info { + struct mtd_info *mtd; + u_int32_t addr; + u_int32_t len; + u_int32_t fail_addr; + u_long time; + u_long retries; + u_int dev; + u_int cell; + void (*callback) (struct erase_info *self); + u_long priv; + u_char state; + struct erase_info *next; +}; + +struct mtd_erase_region_info { + u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ + u_int32_t erasesize; /* For this region */ + u_int32_t numblocks; /* Number of blocks of erasesize in this region */ +}; + +struct mtd_info { + u_char type; + u_int32_t flags; + u_int32_t size; // Total size of the MTD + + /* "Major" erase size for the device. Naïve users may take this + * to be the only erase size available, or may use the more detailed + * information below if they desire + */ + u_int32_t erasesize; + + u_int32_t oobblock; // Size of OOB blocks (e.g. 512) + u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) + u_int32_t ecctype; + u_int32_t eccsize; + + /* + * Reuse some of the above unused fields in the case of NOR flash + * with configurable programming regions to avoid modifying the + * user visible structure layout/size. Only valid when the + * MTD_PROGRAM_REGIONS flag is set. + * (Maybe we should have an union for those?) + */ +#define MTD_PROGREGION_SIZE(mtd) (mtd)->oobblock +#define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize +#define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype + + // Kernel-only stuff starts here. + char *name; + int index; + + // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) + struct nand_oobinfo oobinfo; + u_int32_t oobavail; // Number of bytes in OOB area available for fs + + /* Data for variable erase regions. If numeraseregions is zero, + * it means that the whole device has erasesize as given above. + */ + int numeraseregions; + struct mtd_erase_region_info *eraseregions; + + /* This really shouldn't be here. It can go away in 2.5 */ + u_int32_t bank_size; + + int (*erase) (struct mtd_info *mtd, struct erase_info *instr); + + /* This stuff for eXecute-In-Place */ + int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); + + /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ + void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); + + + int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + + int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + + int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + + /* + * Methods to access the protection register area, present in some + * flash devices. The user data is one time programmable but the + * factory data is read only. + */ + int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); + int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); + int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); + +#if NAND_KVEC_SUPPORT + /* kvec-based read/write methods. We need these especially for NAND flash, + with its limited number of write cycles per erase. + NB: The 'count' parameter is the number of _vectors_, each of + which contains an (ofs, len) tuple. + */ + int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); + int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, + size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); + int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, + size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +#endif + + /* Sync */ + void (*sync) (struct mtd_info *mtd); + + /* Chip-supported device locking */ + int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); + int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); + + /* Power Management functions */ + int (*suspend) (struct mtd_info *mtd); + void (*resume) (struct mtd_info *mtd); + + /* Bad block management functions */ + int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); + int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); + +#if 0 /* don't know what this is for */ + struct notifier_block reboot_notifier; /* default mode before reboot */ +#endif + + void *priv; + + struct module *owner; + int usecount; +}; + +#if 0 /* don't need these */ + /* Kernel-side ioctl definitions */ + +extern int add_mtd_device(struct mtd_info *mtd); +extern int del_mtd_device (struct mtd_info *mtd); + +extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + +extern void put_mtd_device(struct mtd_info *mtd); + + +struct mtd_notifier { + void (*add)(struct mtd_info *mtd); + void (*remove)(struct mtd_info *mtd); + struct list_head list; +}; + + +extern void register_mtd_user (struct mtd_notifier *new); +extern int unregister_mtd_user (struct mtd_notifier *old); +#endif + +#if NAND_KVEC_SUPPORT +int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); + +int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, + unsigned long count, loff_t from, size_t *retlen); +#endif + +#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) +#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) +#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) +#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) +#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) +#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args) +#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args) +#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args) +#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args) +#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) +#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) +#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) + + +#ifdef CONFIG_MTD_PARTITIONS +void mtd_erase_callback(struct erase_info *instr); +#else +static inline void mtd_erase_callback(struct erase_info *instr) +{ + if (instr->callback) + instr->callback(instr); +} +#endif + +/* + * Debugging macro and defines + */ +#define MTD_DEBUG_LEVEL0 (0) /* Quiet */ +#define MTD_DEBUG_LEVEL1 (1) /* Audible */ +#define MTD_DEBUG_LEVEL2 (2) /* Loud */ +#define MTD_DEBUG_LEVEL3 (3) /* Noisy */ + +#ifdef CONFIG_MTD_DEBUG +#define DEBUG(n, args...) \ + do { \ + if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ + printk(KERN_INFO args); \ + } while(0) +#else /* CONFIG_MTD_DEBUG */ +#define DEBUG(n, args...) do { } while(0) + +#endif /* CONFIG_MTD_DEBUG */ + +#endif /* __MTD_MTD_H__ */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand.h --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand.h 2006-11-03 11:35:52.000000000 +0100 @@ -0,0 +1,521 @@ +/* + * linux/include/linux/mtd/nand.h + * + * Copyright (c) 2000 David Woodhouse + * Steven J. Hill + * Thomas Gleixner + * + * $Id: nand.h,v 1.4 2006/11/03 10:35:52 pkj Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Info: + * Contains standard defines and IDs for NAND flash devices + * + * Changelog: + * 01-31-2000 DMW Created + * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers + * so it can be used by other NAND flash device + * drivers. I also changed the copyright since none + * of the original contents of this file are specific + * to DoC devices. David can whack me with a baseball + * bat later if I did something naughty. + * 10-11-2000 SJH Added private NAND flash structure for driver + * 10-24-2000 SJH Added prototype for 'nand_scan' function + * 10-29-2001 TG changed nand_chip structure to support + * hardwarespecific function for accessing control lines + * 02-21-2002 TG added support for different read/write adress and + * ready/busy line access function + * 02-26-2002 TG added chip_delay to nand_chip structure to optimize + * command delay times for different chips + * 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate + * defines in jffs2/wbuf.c + * 08-07-2002 TG forced bad block location to byte 5 of OOB, even if + * CONFIG_MTD_NAND_ECC_JFFS2 is not set + * 08-10-2002 TG extensions to nand_chip structure to support HW-ECC + * + * 08-29-2002 tglx nand_chip structure: data_poi for selecting + * internal / fs-driver buffer + * support for 6byte/512byte hardware ECC + * read_ecc, write_ecc extended for different oob-layout + * oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB, + * NAND_YAFFS_OOB + * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL + * Split manufacturer and device ID structures + * + * 02-08-2004 tglx added option field to nand structure for chip anomalities + * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id + * update of nand_chip structure description + * 01-17-2005 dmarlin added extended commands for AG-AND device and added option + * for BBT_AUTO_REFRESH. + * 01-20-2005 dmarlin added optional pointer to hardware specific callback for + * extra error status checks. + */ +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H + +#if 0 /* avoid these as much as possible */ +#include +#include +#include +#endif + +#include "mtd.h" /* local */ + +#include /* we do need this though ... */ + +struct mtd_info; +/* Scan and identify a NAND device */ +extern int nand_scan (struct mtd_info *mtd, int max_chips); +/* Free resources held by the NAND device */ +extern void nand_release (struct mtd_info *mtd); + +/* Read raw data from the device without ECC */ +extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); + + +/* The maximum number of NAND chips in an array */ +#define NAND_MAX_CHIPS 8 + +/* This constant declares the max. oobsize / page, which + * is supported now. If you add a chip with bigger oobsize/page + * adjust this accordingly. + */ +#define NAND_MAX_OOBSIZE 64 + +/* + * Constants for hardware specific CLE/ALE/NCE function +*/ +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 +/* Set write protection by setting WP to high. Not used! */ +#define NAND_CTL_SETWP 7 +/* Clear write protection by setting WP to low. Not used! */ +#define NAND_CTL_CLRWP 8 + +/* + * Standard NAND flash commands + */ +#define NAND_CMD_READ0 0 +#define NAND_CMD_READ1 1 +#define NAND_CMD_PAGEPROG 0x10 +#define NAND_CMD_READOOB 0x50 +#define NAND_CMD_ERASE1 0x60 +#define NAND_CMD_STATUS 0x70 +#define NAND_CMD_STATUS_MULTI 0x71 +#define NAND_CMD_SEQIN 0x80 +#define NAND_CMD_READID 0x90 +#define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_RESET 0xff + +/* Extended commands for large page devices */ +#define NAND_CMD_READSTART 0x30 +#define NAND_CMD_CACHEDPROG 0x15 + +/* Extended commands for AG-AND device */ +/* + * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but + * there is no way to distinguish that from NAND_CMD_READ0 + * until the remaining sequence of commands has been completed + * so add a high order bit and mask it off in the command. + */ +#define NAND_CMD_DEPLETE1 0x100 +#define NAND_CMD_DEPLETE2 0x38 +#define NAND_CMD_STATUS_MULTI 0x71 +#define NAND_CMD_STATUS_ERROR 0x72 +/* multi-bank error status (banks 0-3) */ +#define NAND_CMD_STATUS_ERROR0 0x73 +#define NAND_CMD_STATUS_ERROR1 0x74 +#define NAND_CMD_STATUS_ERROR2 0x75 +#define NAND_CMD_STATUS_ERROR3 0x76 +#define NAND_CMD_STATUS_RESET 0x7f +#define NAND_CMD_STATUS_CLEAR 0xff + +/* Status bits */ +#define NAND_STATUS_FAIL 0x01 +#define NAND_STATUS_FAIL_N1 0x02 +#define NAND_STATUS_TRUE_READY 0x20 +#define NAND_STATUS_READY 0x40 +#define NAND_STATUS_WP 0x80 + +/* + * Constants for ECC_MODES + */ + +/* No ECC. Usage is not recommended ! */ +#define NAND_ECC_NONE 0 +/* Software ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_SOFT 1 +/* Hardware ECC 3 byte ECC per 256 Byte data */ +#define NAND_ECC_HW3_256 2 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW3_512 3 +/* Hardware ECC 3 byte ECC per 512 Byte data */ +#define NAND_ECC_HW6_512 4 +/* Hardware ECC 8 byte ECC per 512 Byte data */ +#define NAND_ECC_HW8_512 6 +/* Hardware ECC 12 byte ECC per 2048 Byte data */ +#define NAND_ECC_HW12_2048 7 + +/* + * Constants for Hardware ECC + */ +/* Reset Hardware ECC for read */ +#define NAND_ECC_READ 0 +/* Reset Hardware ECC for write */ +#define NAND_ECC_WRITE 1 +/* Enable Hardware ECC before syndrom is read back from flash */ +#define NAND_ECC_READSYN 2 + +/* Bit mask for flags passed to do_nand_read_ecc */ +#define NAND_GET_DEVICE 0x80 + + +/* Option constants for bizarre disfunctionality and real +* features +*/ +/* Chip can not auto increment pages */ +#define NAND_NO_AUTOINCR 0x00000001 +/* Buswitdh is 16 bit */ +#define NAND_BUSWIDTH_16 0x00000002 +/* Device supports partial programming without padding */ +#define NAND_NO_PADDING 0x00000004 +/* Chip has cache program function */ +#define NAND_CACHEPRG 0x00000008 +/* Chip has copy back function */ +#define NAND_COPYBACK 0x00000010 +/* AND Chip which has 4 banks and a confusing page / block + * assignment. See Renesas datasheet for further information */ +#define NAND_IS_AND 0x00000020 +/* Chip has a array of 4 pages which can be read without + * additional ready /busy waits */ +#define NAND_4PAGE_ARRAY 0x00000040 +/* Chip requires that BBT is periodically rewritten to prevent + * bits from adjacent blocks from 'leaking' in altering data. + * This happens with the Renesas AG-AND chips, possibly others. */ +#define BBT_AUTO_REFRESH 0x00000080 + +/* Options valid for Samsung large page devices */ +#define NAND_SAMSUNG_LP_OPTIONS \ + (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) + +/* Macros to identify the above */ +#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR)) +#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) +#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) +#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) + +/* Mask to zero out the chip options, which come from the id table */ +#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) + +/* Non chip related options */ +/* Use a flash based bad block table. This option is passed to the + * default bad block table function. */ +#define NAND_USE_FLASH_BBT 0x00010000 +/* The hw ecc generator provides a syndrome instead a ecc value on read + * This can only work if we have the ecc bytes directly behind the + * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ +#define NAND_HWECC_SYNDROME 0x00020000 +/* This option skips the bbt scan during initialization. */ +#define NAND_SKIP_BBTSCAN 0x00040000 + +/* Options set by nand scan */ +/* Nand scan has allocated oob_buf */ +#define NAND_OOBBUF_ALLOC 0x40000000 +/* Nand scan has allocated data_buf */ +#define NAND_DATABUF_ALLOC 0x80000000 + + +/* + * nand_state_t - chip states + * Enumeration for NAND flash chip state + */ +typedef enum { + FL_READY, + FL_READING, + FL_WRITING, + FL_ERASING, + FL_SYNCING, + FL_CACHEDPRG, + FL_PM_SUSPENDED, +} nand_state_t; + +/* Keep gcc happy */ +struct nand_chip; + +/** + * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices + * @lock: protection lock + * @active: the mtd device which holds the controller currently + * @wq: wait queue to sleep on if a NAND operation is in progress + * used instead of the per chip wait queue when a hw controller is available + */ +#if NAND_HWECC_SUPPORT +struct nand_hw_control { + spinlock_t lock; + struct nand_chip *active; + wait_queue_head_t wq; +}; +#endif + +/** + * struct nand_chip - NAND Private Flash Chip Data + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @read_byte: [REPLACEABLE] read one byte from the chip + * @write_byte: [REPLACEABLE] write one byte to the chip + * @read_word: [REPLACEABLE] read one word from the chip + * @write_word: [REPLACEABLE] write one word to the chip + * @write_buf: [REPLACEABLE] write data from the buffer to the chip + * @read_buf: [REPLACEABLE] read data from the chip into the buffer + * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data + * @select_chip: [REPLACEABLE] select chip nr + * @block_bad: [REPLACEABLE] check, if the block is bad + * @block_markbad: [REPLACEABLE] mark the block bad + * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line + * If set to NULL no access to ready/busy is available and the ready/busy information + * is read from the chip status register + * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip + * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready + * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware + * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) + * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only + * be provided if a hardware ECC is available + * @erase_cmd: [INTERN] erase command write function, selectable due to AND support + * @scan_bbt: [REPLACEABLE] function to scan bad block table + * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines + * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step + * @eccsteps: [INTERN] number of ecc calculation steps per page + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) + * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip + * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress + * @state: [INTERN] the current state of the NAND device + * @page_shift: [INTERN] number of address bits in a page (column address bits) + * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock + * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry + * @chip_shift: [INTERN] number of address bits in one chip + * @data_buf: [INTERN] internal buffer for one page + oob + * @oob_buf: [INTERN] oob buffer for one eraseblock + * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized + * @data_poi: [INTERN] pointer to a data buffer + * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about + * special functionality. See the defines for further explanation + * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @numchips: [INTERN] number of physical chips + * @chipsize: [INTERN] the size of one chip for multichip arrays + * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 + * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf + * @autooob: [REPLACEABLE] the default (auto)placement scheme + * @bbt: [INTERN] bad block table pointer + * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup + * @bbt_md: [REPLACEABLE] bad block table mirror descriptor + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan + * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices + * @priv: [OPTIONAL] pointer to private chip date + * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks + * (determine if errors are correctable) + */ + +struct nand_chip { + void __iomem *IO_ADDR_R; + void __iomem *IO_ADDR_W; + + u_char (*read_byte)(struct mtd_info *mtd); + void (*write_byte)(struct mtd_info *mtd, u_char byte); + u16 (*read_word)(struct mtd_info *mtd); + void (*write_word)(struct mtd_info *mtd, u16 word); + + void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); + int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); + int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + void (*enable_hwecc)(struct mtd_info *mtd, int mode); + void (*erase_cmd)(struct mtd_info *mtd, int page); + int (*scan_bbt)(struct mtd_info *mtd); + int eccmode; + int eccsize; + int eccbytes; + int eccsteps; + int chip_delay; +#if 0 /* no spinlocks or wait queues in boot loader */ + spinlock_t chip_lock; + wait_queue_head_t wq; +#endif + nand_state_t state; + int page_shift; + int phys_erase_shift; + int bbt_erase_shift; + int chip_shift; + u_char *data_buf; + u_char *oob_buf; + int oobdirty; + u_char *data_poi; + unsigned int options; + int badblockpos; + int numchips; + unsigned long chipsize; + int pagemask; + int pagebuf; + struct nand_oobinfo *autooob; + uint8_t *bbt; + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; + struct nand_hw_control *controller; + void *priv; + int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); +}; + +/* + * NAND Flash Manufacturer ID Codes + */ +#define NAND_MFR_TOSHIBA 0x98 +#define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f +#define NAND_MFR_RENESAS 0x07 +#define NAND_MFR_STMICRO 0x20 +#define NAND_MFR_HYNIX 0xad + +/** + * struct nand_flash_dev - NAND Flash Device ID Structure + * + * @name: Identify the device type + * @id: device ID code + * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 + * If the pagesize is 0, then the real pagesize + * and the eraseize are determined from the + * extended id bytes in the chip + * @erasesize: Size of an erase block in the flash device. + * @chipsize: Total chipsize in Mega Bytes + * @options: Bitfield to store chip relevant options + */ +struct nand_flash_dev { + char *name; + int id; + unsigned long pagesize; + unsigned long chipsize; + unsigned long erasesize; + unsigned long options; +}; + +/** + * struct nand_manufacturers - NAND Flash Manufacturer ID Structure + * @name: Manufacturer name + * @id: manufacturer ID code of device. +*/ +struct nand_manufacturers { + int id; + char * name; +}; + +extern struct nand_flash_dev nand_flash_ids[]; +extern struct nand_manufacturers nand_manuf_ids[]; + +/** + * struct nand_bbt_descr - bad block table descriptor + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE + * when bbt is searched, then we store the found bbts pages here. + * Its an array and supports up to 8 chips now + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob are of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This number of + * blocks is reserved at the end of the device where the tables are + * written. + * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than + * bad) block in the stored bbt + * @pattern: pattern to identify bad block table or factory marked good / + * bad blocks, can be NULL, if len = 0 + * + * Descriptor for the bad block table marker and the descriptor for the + * pattern which identifies good and bad blocks. The assumption is made + * that the pattern and the version count are always located in the oob area + * of the first block. + */ +struct nand_bbt_descr { + int options; + int pages[NAND_MAX_CHIPS]; + int offs; + int veroffs; + uint8_t version[NAND_MAX_CHIPS]; + int len; + int maxblocks; + int reserved_block_code; + uint8_t *pattern; +}; + +/* Options for the bad block table descriptors */ + +/* The number of bits used per block in the bbt on the device */ +#define NAND_BBT_NRBITS_MSK 0x0000000F +#define NAND_BBT_1BIT 0x00000001 +#define NAND_BBT_2BIT 0x00000002 +#define NAND_BBT_4BIT 0x00000004 +#define NAND_BBT_8BIT 0x00000008 +/* The bad block table is in the last good block of the device */ +#define NAND_BBT_LASTBLOCK 0x00000010 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_ABSPAGE 0x00000020 +/* The bbt is at the given page, else we must scan for the bbt */ +#define NAND_BBT_SEARCH 0x00000040 +/* bbt is stored per chip on multichip devices */ +#define NAND_BBT_PERCHIP 0x00000080 +/* bbt has a version counter at offset veroffs */ +#define NAND_BBT_VERSION 0x00000100 +/* Create a bbt if none axists */ +#define NAND_BBT_CREATE 0x00000200 +/* Search good / bad pattern through all pages of a block */ +#define NAND_BBT_SCANALLPAGES 0x00000400 +/* Scan block empty during good / bad block scan */ +#define NAND_BBT_SCANEMPTY 0x00000800 +/* Write bbt if neccecary */ +#define NAND_BBT_WRITE 0x00001000 +/* Read and write back block contents when writing bbt */ +#define NAND_BBT_SAVECONTENT 0x00002000 +/* Search good / bad pattern on the first and the second page */ +#define NAND_BBT_SCAN2NDPAGE 0x00004000 + +/* The maximum number of blocks to scan for a bbt */ +#define NAND_BBT_SCAN_MAXBLOCKS 4 + +extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); +extern int nand_default_bbt (struct mtd_info *mtd); +extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); +extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); +extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags); + +/* +* Constants for oob configuration +*/ +#define NAND_SMALL_BADBLOCK_POS 5 +#define NAND_LARGE_BADBLOCK_POS 0 + +#endif /* __LINUX_MTD_NAND_H */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_base.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_base.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_base.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_base.c 2006-11-10 09:55:58.000000000 +0100 @@ -0,0 +1,2910 @@ +/* + * Snitched from drivers/mtd/nand_base.c + * Modified to run outside Linux. + * #if 0'd to remove non-essential functionality + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 02-08-2004 tglx: support for strange chips, which cannot auto increment + * pages on read / read_oob + * + * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes + * pointed this out, as he marked an auto increment capable chip + * as NOAUTOINCR in the board driver. + * Make reads over block boundaries work too + * + * 04-14-2004 tglx: first working version for 2k page size chips + * + * 05-19-2004 tglx: Basic support for Renesas AG-AND chips + * + * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared + * among multiple independend devices. Suggestions and initial patch + * from Ben Dooks + * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * + * 08-20-2005 vwool: suspend/resume added + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ecc support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * + * $Id: nand_base.c,v 1.11 2006/11/10 08:55:58 ricardw Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#if 0 +#include +#include +#endif + +#include "mtd.h" +#include "nand.h" +#include "nand_ecc.h" + +#if 0 +#include +#include +#include +#include +#endif + +#ifdef CONFIG_MTD_PARTITIONS +#include + +#endif + +#include "lib.h" + +#define GPIO_SYNC 0 + +#undef DEBUG /* from mtd.h */ +#define DEBUG(n, args...) do { } while(0) + +#define D(x) + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_oobinfo nand_oob_8 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { {3, 2}, {6, 2} } +}; + +static struct nand_oobinfo nand_oob_16 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { {8, 8} } +}; + +static struct nand_oobinfo nand_oob_64 = { + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } +}; + +/* This is used for padding purposes in nand_write_oob */ +#if NAND_WRITE_SUPPORT +static u_char ffchars[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +#endif + +/* + * NAND low-level MTD interface functions + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); + +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); +#if NAND_KVEC_SUPPORT +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen); +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); +#endif +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); +static void nand_sync (struct mtd_info *mtd); + +/* Some internal functions */ +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, + struct nand_oobinfo *oobsel, int mode); +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode); +#else +#define nand_verify_pages(...) (0) +#endif + +static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state); + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +static void nand_release_device (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* De-select the NAND device */ + this->select_chip(mtd, -1); +#if 0 + + if (this->controller) { + /* Release the controller and the chip */ + spin_lock(&this->controller->lock); + this->controller->active = NULL; + this->state = FL_READY; + wake_up(&this->controller->wq); + spin_unlock(&this->controller->lock); + } else { + /* Release the chip */ + spin_lock(&this->chip_lock); + this->state = FL_READY; + wake_up(&this->wq); + spin_unlock(&this->chip_lock); + } +#endif +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswith + */ +static u_char nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readb(this->IO_ADDR_R); +} + +/** + * nand_write_byte - [DEFAULT] write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 8it buswith + */ +static void nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); + +#if GPIO_SYNC + /* Bus sync: Read from address we just wrote. + * This generates no signal to the NAND flash, since only chip + * select lines are pulled out to the chip, and read is not + * gated with chip select for the write area. + * Turns out this is (probably) the wrong way to do it; the + * right way is to set up suitable wait states in the kernel + * config (CONFIG_ETRAX_MEM_GRP3_CONFIG). + */ + (void) readb(this->IO_ADDR_W); /* that's right, IO_ADDR_W */ +#endif +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith with + * endianess conversion + */ +static u_char nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); +} + +/** + * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * Default write function for 16bit buswith with + * endianess conversion + */ +static void nand_write_byte16(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writew(le16_to_cpu((u16) byte), this->IO_ADDR_W); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith without + * endianess conversion + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readw(this->IO_ADDR_R); +} + +/** + * nand_write_word - [DEFAULT] write one word to the chip + * @mtd: MTD device structure + * @word: data word to write + * + * Default write function for 16bit buswith without + * endianess conversion + */ +static void nand_write_word(struct mtd_info *mtd, u16 word) +{ + struct nand_chip *this = mtd->priv; + writew(word, this->IO_ADDR_W); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chip: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + switch(chip) { + case -1: + this->hwcontrol(mtd, NAND_CTL_CLRNCE); + break; + case 0: + this->hwcontrol(mtd, NAND_CTL_SETNCE); + break; + + default: + BUG(); + } +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswith + */ +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_W); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswith + */ +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 8bit buswith + */ +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_W); + +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswith + */ +static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R); +} + +/** + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 16bit buswith + */ +static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i=0; iIO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int page, chipnr, res = 0; + struct nand_chip *this = mtd->priv; + u16 bad; + + if (getchip) { + page = (int)(ofs >> this->page_shift); + chipnr = (int)(ofs >> this->chip_shift); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + } else + page = (int) ofs; + + if (this->options & NAND_BUSWIDTH_16) { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask); + bad = cpu_to_le16(this->read_word(mtd)); + if (this->badblockpos & 0x1) + bad >>= 8; + if ((bad & 0xFF) != 0xff) + res = 1; + } else { + this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask); + if (this->read_byte(mtd) != 0xff) + res = 1; + } + + if (getchip) { + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + } + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. +*/ +#if NAND_WRITE_SUPPORT +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + u_char buf[2] = {0, 0}; + size_t retlen; + int block; + + /* Get block number */ + block = ((int) ofs) >> this->bbt_erase_shift; + if (this->bbt) + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + +#if NAND_BBT_SUPPORT + /* Do we have a flash based bad block table ? */ + if (this->options & NAND_USE_FLASH_BBT) + return nand_update_bbt (mtd, ofs); +#endif + + /* We write two bytes, so we dont have to mess with 16 bit access */ + ofs += mtd->oobsize + (this->badblockpos & ~0x01); + return nand_write_oob (mtd, ofs , 2, &retlen, buf); +} +#endif + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * Check, if the device is write protected + * + * The function expects, that the device is already selected + */ +#if NAND_WRITE_SUPPORT +static int nand_check_wp (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} +#endif + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + + if (!this->bbt) + return this->block_bad(mtd, ofs, getchip); + +#if NAND_BBT_SUPPORT + /* Return info from the table */ + return nand_isbad_bbt (mtd, ofs, allowbbt); +#endif + BUG(); /* should not happen */ +} + +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +static void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; +#if 0 + unsigned long timeo = jiffies + 2; +#endif + + /* wait until command is processed or timeout occures */ + do { + if (this->dev_ready(mtd)) + return; +#if 0 + touch_softlockup_watchdog(); + } while (time_before(jiffies, timeo)); +#endif + } while (1); +} + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page + * devices (256/512 Bytes per page) + */ +static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + this->write_byte(mtd, readcmd); + } + this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + nand_wait_ready(mtd); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page devices + * We dont have the seperate regions as we have in the small page devices. + * We must emulate NAND_CMD_READOOB to keep the code compatible. + * + */ +static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->oobblock; + command = NAND_CMD_READ0; + } + + + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the command to the device. */ + this->write_byte(mtd, (command & 0xff)); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + this->write_byte(mtd, column & 0xff); + this->write_byte(mtd, column >> 8); + } + if (page_addr != -1) { + this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); + this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for devices > 128MiB */ + if (this->chipsize > (128 << 20)) + this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); + } + /* Latch in address */ + this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + this->hwcontrol(mtd, NAND_CTL_SETCLE); + this->write_byte(mtd, NAND_CMD_STATUS); + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); + return; + + case NAND_CMD_READ0: + /* Begin command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* Write out the start read command */ + this->write_byte(mtd, NAND_CMD_READSTART); + /* End command latch cycle */ + this->hwcontrol(mtd, NAND_CTL_CLRCLE); + /* Fall through into ready check */ + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay (this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + nand_wait_ready(mtd); +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @this: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ + this->state = new_state; + return 0; +#if 0 + struct nand_chip *active; + spinlock_t *lock; + wait_queue_head_t *wq; + DECLARE_WAITQUEUE (wait, current); + + lock = (this->controller) ? &this->controller->lock : &this->chip_lock; + wq = (this->controller) ? &this->controller->wq : &this->wq; +retry: + active = this; + spin_lock(lock); + + /* Hardware controller shared among independend devices */ + if (this->controller) { + if (this->controller->active) + active = this->controller->active; + else + this->controller->active = this; + } + if (active == this && this->state == FL_READY) { + this->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + spin_unlock(lock); + return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); + goto retry; +#endif +} + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state to select the max. timeout value + * + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + * +*/ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + +#if 0 + unsigned long timeo = jiffies; +#endif + int status; + +#if 0 + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; +#endif + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay (100); + + if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) + this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + +#if 0 + while (time_before(jiffies, timeo)) { + /* Check, if we were interrupted */ + if (this->state != state) + return 0; +#endif + while (1) { /* wait indefinitely */ + + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) + break; + } + +#if 0 + cond_resched(); +#endif + } + status = (int) this->read_byte(mtd); + return status; +} + +/** + * nand_write_page - [GENERIC] write one page + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @cached: 1 = enable cached programming if supported by chip + * + * Nand_page_program function is used for write and writev ! + * This function will always program a full page of data + * If you call it with a non page aligned buffer, you're lost :) + * + * Cached programming is not supported yet. + */ +#if NAND_WRITE_SUPPORT +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, + u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) +{ + int i, status; + u_char ecc_code[32]; + int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + int *oob_config = oobsel->eccpos; + int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; + int eccbytes = 0; + + /* FIXME: Enable cached programming */ + cached = 0; + + /* Send command to begin auto page programming */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data, take care of eccmode */ + switch (eccmode) { + /* No ecc, write all */ + case NAND_ECC_NONE: + puts ("Writing data without ECC to NAND-FLASH is not recommended\r\n"); + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + + /* Software ecc 3/256, write all */ + case NAND_ECC_SOFT: + for (; eccsteps; eccsteps--) { + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < 3; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + datidx += this->eccsize; + } + this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + default: + eccbytes = this->eccbytes; + for (; eccsteps; eccsteps--) { + /* enable hardware ecc logic for write */ + this->enable_hwecc(mtd, NAND_ECC_WRITE); + this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); + this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); + for (i = 0; i < eccbytes; i++, eccidx++) + oob_buf[oob_config[eccidx]] = ecc_code[i]; + /* If the hardware ecc provides syndromes then + * the ecc code must be written immidiately after + * the data bytes (words) */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, ecc_code, eccbytes); + datidx += this->eccsize; + } + break; + } + + /* Write out OOB data */ + if (this->options & NAND_HWECC_SYNDROME) + this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes); + else + this->write_buf(mtd, oob_buf, mtd->oobsize); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1); + + if (!cached) { + /* call wait ready function */ + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + return -EIO; + } + } else { + /* FIXME: Implement cached programming ! */ + /* wait until cache is ready*/ + // status = this->waitfunc (mtd, this, FL_CACHEDRPG); + } + return 0; +} +#endif + +#if NAND_WRITE_SUPPORT +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE +/** + * nand_verify_pages - [GENERIC] verify the chip contents after a write + * @mtd: MTD device structure + * @this: NAND chip structure + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * @numpages: number of pages to verify + * @oob_buf: out of band data buffer + * @oobsel: out of band selecttion structre + * @chipnr: number of the current chip + * @oobmode: 1 = full buffer verify, 0 = ecc only + * + * The NAND device assumes that it is always writing to a cleanly erased page. + * Hence, it performs its internal write verification only on bits that + * transitioned from 1 to 0. The device does NOT verify the whole page on a + * byte by byte basis. It is possible that the page was not completely erased + * or the page is becoming unusable due to wear. The read with ECC would catch + * the error later when the ECC page check fails, but we would rather catch + * it early in the page write stage. Better to write no data than invalid data. + */ +static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, + u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode) +{ + int i, j, datidx = 0, oobofs = 0, res = -EIO; + int eccsteps = this->eccsteps; + int hweccbytes; + u_char oobdata[64]; + + hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0; + + /* Send command to read back the first page */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + + for(;;) { + for (j = 0; j < eccsteps; j++) { + /* Loop through and verify the data */ + if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + datidx += mtd->eccsize; + /* Have we a hw generator layout ? */ + if (!hweccbytes) + continue; + if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + oobofs += hweccbytes; + } + + /* check, if we must compare all data or if we just have to + * compare the ecc bytes + */ + if (oobmode) { + if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + goto out; + } + } else { + /* Read always, else autoincrement fails */ + this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps); + + if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) { + int ecccnt = oobsel->eccbytes; + + for (i = 0; i < ecccnt; i++) { + int idx = oobsel->eccpos[i]; + if (oobdata[idx] != oob_buf[oobofs + idx] ) { + DEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed ECC write " + "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + goto out; + } + } + } + } + oobofs += mtd->oobsize - hweccbytes * eccsteps; + page++; + numpages--; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + * Do this also before returning, so the chip is + * ready for the next command. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* All done, return happy */ + if (!numpages) + return 0; + + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this)) + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + } + /* + * Terminate the read command. We come here in case of an error + * So we must issue a reset command. + */ +out: + this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1); + return res; +} +#endif +#endif + +/** + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff + */ +static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); +} + + +/** + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * This function simply calls nand_do_read_ecc with flags = 0xff + */ +static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) +{ + /* use userspace supplied oobinfo, if zero */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer (can be NULL) + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags) +{ + + int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; + int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; + struct nand_chip *this = mtd->priv; + u_char *data_poi, *oob_data = oob_buf; + u_char ecc_calc[32]; + u_char ecc_code[32]; + int eccmode, eccsteps; + int *oob_config, datidx; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + int eccbytes; + int compareecc = 1; + int oobreadlen; + + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + D( + puts ("nand_read_ecc: from = "); + putx (from); + puts (", len = "); + putx(len); + putnl(); + ) + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); + D(puts("nand_read_ecc: Attempt read beyond end of device\r\n")); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + if (flags & NAND_GET_DEVICE) + nand_get_device (this, mtd, FL_READING); + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) + oobsel = this->autooob; + + eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; + oob_config = oobsel->eccpos; + + /* Select the NAND device */ + chipnr = (int)(from >> this->chip_shift); + this->select_chip(mtd, chipnr); + + /* First we calculate the starting page */ + realpage = (int) (from >> this->page_shift); + page = realpage & this->pagemask; + + /* Get raw starting column */ + col = from & (mtd->oobblock - 1); + + end = mtd->oobblock; + ecc = this->eccsize; + eccbytes = this->eccbytes; + + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) + compareecc = 0; + + oobreadlen = mtd->oobsize; + if (this->options & NAND_HWECC_SYNDROME) + oobreadlen -= oobsel->eccbytes; + + /* Loop until all data read */ + while (read < len) { + + int aligned = (!col && (len - read) >= end); + /* + * If the read is not page aligned, we have to read into data buffer + * due to ecc, else we read into return buffer direct + */ + if (aligned) + data_poi = &buf[read]; + else + data_poi = this->data_buf; + + /* Check, if we have this page in the buffer + * + * FIXME: Make it work when we must provide oob data too, + * check the usage of data_buf oob field + */ + if (realpage == this->pagebuf && !oob_buf) { + /* aligned read ? */ + if (aligned) + memcpy (data_poi, this->data_buf, end); + goto readdata; + } + + /* Check, if we must send the read command */ + if (sndcmd) { + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + /* get oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || + oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + oob_data = &this->data_buf[end]; + + eccsteps = this->eccsteps; + + switch (eccmode) { + case NAND_ECC_NONE: { /* No ECC, Read in a page */ +#if 0 + static unsigned long lastwhinge = 0; + if ((lastwhinge / HZ) != (jiffies / HZ)) { + puts ("Reading data from NAND FLASH without ECC is not recommended\r\n"); + lastwhinge = jiffies; + } +#endif + this->read_buf(mtd, data_poi, end); + break; + } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ + this->read_buf(mtd, data_poi, end); + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + break; + + default: +#if NAND_HWECC_SUPPORT + for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { + this->enable_hwecc(mtd, NAND_ECC_READ); + this->read_buf(mtd, &data_poi[datidx], ecc); + + /* HW ecc with syndrome calculation must read the + * syndrome from flash immidiately after the data */ + if (!compareecc) { + /* Some hw ecc generators need to know when the + * syndrome is read from flash */ + this->enable_hwecc(mtd, NAND_ECC_READSYN); + this->read_buf(mtd, &oob_data[i], eccbytes); + /* We calc error correction directly, it checks the hw + * generator for an error, reads back the syndrome and + * does the error correction on the fly */ + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " + "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); + ecc_failed++; + } + } else { + this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); + } + } +#endif + break; + } + + /* read oobdata */ + this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen); + + /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */ + if (!compareecc) + goto readoob; + + /* Pick the ECC bytes out of the oob data */ + for (j = 0; j < oobsel->eccbytes; j++) + ecc_code[j] = oob_data[oob_config[j]]; + + /* correct data, if neccecary */ + for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]); + + /* Get next chunk of ecc bytes */ + j += eccbytes; + + /* Check, if we have a fs supplied oob-buffer, + * This is the legacy mode. Used by YAFFS1 + * Should go away some day + */ + if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { + int *p = (int *)(&oob_data[mtd->oobsize]); + p[i] = ecc_status; + } + + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + D( + puts ("nand_read_ecc: " "Failed ECC read, page "); + putx (page); + putnl(); + ) + ecc_failed++; + } + } + + readoob: + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + /* without autoplace. Legacy mode used by YAFFS1 */ + switch(oobsel->useecc) { + case MTD_NANDECC_AUTOPLACE: + case MTD_NANDECC_AUTOPL_USR: + /* Walk through the autoplace chunks */ + for (i = 0; oobsel->oobfree[i][1]; i++) { + int from = oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy(&oob_buf[oob], &oob_data[from], num); + oob += num; + } + break; + case MTD_NANDECC_PLACE: + /* YAFFS1 legacy mode */ + oob_data += this->eccsteps * sizeof (int); + default: + oob_data += mtd->oobsize; + } + } + readdata: + /* Partial page read, transfer data into fs buffer */ + if (!aligned) { + for (j = col; j < end && read < len; j++) + buf[read++] = data_poi[j]; + this->pagebuf = realpage; + } else + read += mtd->oobblock; + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + if (read == len) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + if (flags & NAND_GET_DEVICE) + nand_release_device(mtd); + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + *retlen = read; + return ecc_failed ? -EBADMSG : 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * NAND read out-of-band data from the spare area + */ +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) +{ + int i, col, page, chipnr; + struct nand_chip *this = mtd->priv; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + /* Shift to get page */ + page = (int)(from >> this->page_shift); + chipnr = (int)(from >> this->chip_shift); + + /* Mask to get column */ + col = from & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); + *retlen = 0; + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask); + /* + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ + i = 0; + while (i < len) { + int thislen = mtd->oobsize - col; + thislen = min_t(int, thislen, len); + this->read_buf(mtd, &buf[i], thislen); + i += thislen; + + /* Read more ? */ + if (i < len) { + page++; + col = 0; + + /* Check, if we cross a chip boundary */ + if (!(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + + /* Apply delay or wait for ready/busy pin + * Do this before the AUTOINCR check, so no problems + * arise if a chip which does auto increment + * is marked as NOAUTOINCR by the board driver. + */ + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) { + /* For subsequent page reads set offset to 0 */ + this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask); + } + } + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Return happy */ + *retlen = len; + return 0; +} + +/** + * nand_read_raw - [GENERIC] Read raw data including oob into buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @from: offset to read from + * @len: number of bytes to read + * @ooblen: number of oob data bytes to read + * + * Read raw data including oob into buffer + */ +int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen) +{ + struct nand_chip *this = mtd->priv; + int page = (int) (from >> this->page_shift); + int chip = (int) (from >> this->chip_shift); + int sndcmd = 1; + int cnt = 0; + int pagesize = mtd->oobblock + mtd->oobsize; + int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd , FL_READING); + + this->select_chip (mtd, chip); + + /* Add requested oob length */ + len += ooblen; + + while (len) { + if (sndcmd) + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); + sndcmd = 0; + + this->read_buf (mtd, &buf[cnt], pagesize); + + len -= pagesize; + cnt += pagesize; + page++; + + if (!this->dev_ready) + udelay (this->chip_delay); + else + nand_wait_ready(mtd); + + /* Check, if the chip supports auto page increment */ + if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) + sndcmd = 1; + } + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + return 0; +} + + +/** + * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer + * @mtd: MTD device structure + * @fsbuf: buffer given by fs driver + * @oobsel: out of band selection structre + * @autoplace: 1 = place given buffer into the oob bytes + * @numpages: number of pages to prepare + * + * Return: + * 1. Filesystem buffer available and autoplacement is off, + * return filesystem buffer + * 2. No filesystem buffer or autoplace is off, return internal + * buffer + * 3. Filesystem buffer is given and autoplace selected + * put data from fs buffer into internal buffer and + * retrun internal buffer + * + * Note: The internal buffer is filled with 0xff. This must + * be done only once, when no autoplacement happens + * Autoplacement sets the buffer dirty flag, which + * forces the 0xff fill before using the buffer again. + * +*/ +#if NAND_WRITE_SUPPORT +static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, + int autoplace, int numpages) +{ + struct nand_chip *this = mtd->priv; + int i, len, ofs; + + /* Zero copy fs supplied buffer */ + if (fsbuf && !autoplace) + return fsbuf; + + /* Check, if the buffer must be filled with ff again */ + if (this->oobdirty) { + memset (this->oob_buf, 0xff, + mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + this->oobdirty = 0; + } + + /* If we have no autoplacement or no fs buffer use the internal one */ + if (!autoplace || !fsbuf) + return this->oob_buf; + + /* Walk through the pages and place the data */ + this->oobdirty = 1; + ofs = 0; + while (numpages--) { + for (i = 0, len = 0; len < mtd->oobavail; i++) { + int to = ofs + oobsel->oobfree[i][0]; + int num = oobsel->oobfree[i][1]; + memcpy (&this->oob_buf[to], fsbuf, num); + len += num; + fsbuf += num; + } + ofs += mtd->oobavail; + } + return this->oob_buf; +} +#endif + +#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 + +/** + * nand_write - [MTD Interface] compability function for nand_write_ecc + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL + * +*/ +#if NAND_WRITE_SUPPORT +static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); +} +#endif + +/** + * nand_write_ecc - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND write with ECC + */ +#if NAND_WRITE_SUPPORT +static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) +{ + int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; + int autoplace = 0, numpages, totalpages; + struct nand_chip *this = mtd->priv; + u_char *oobbuf, *bufstart; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + *retlen = 0; + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(len)) { + puts ("nand_write_ecc: Attempt to write not page aligned data\r\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Calculate chipnr */ + chipnr = (int)(to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; + + /* Setup variables and oob buffer */ + totalpages = len >> this->page_shift; + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) + this->pagebuf = -1; + + /* Set it relative to chip */ + page &= this->pagemask; + startpage = page; + /* Calc number of pages we can write in one go */ + numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages); + bufstart = (u_char *)buf; + + /* Loop until all data is written */ + while (written < len) { + + this->data_poi = (u_char*) &buf[written]; + /* Write one page. If this is the last page to write + * or the last page in this block, then use the + * real pageprogram command, else select cached programming + * if supported by the chip. + */ + ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret); + goto out; + } + /* Next oob page */ + oob += mtd->oobsize; + /* Update written bytes count */ + written += mtd->oobblock; + if (written == len) + goto cmp; + + /* Increment page address */ + page++; + + /* Have we hit a block boundary ? Then we have to verify and + * if verify is ok, we have to setup the oob buffer for + * the next pages. + */ + if (!(page & (ppblock - 1))){ + int ofs; + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, + page - startpage, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (ret) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + goto out; + } + *retlen = written; + + ofs = autoplace ? mtd->oobavail : mtd->oobsize; + if (eccbuf) + eccbuf += (page - startpage) * ofs; + totalpages -= page - startpage; + numpages = min (totalpages, ppblock); + page &= this->pagemask; + startpage = page; + oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, + autoplace, numpages); + oob = 0; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + } + /* Verify the remaining pages */ +cmp: + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, totalpages, + oobbuf, oobsel, chipnr, (eccbuf != NULL)); + if (!ret) + *retlen = written; + else + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret); + +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} +#endif + + +/** + * nand_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write out-of-band + */ +#if NAND_WRITE_SUPPORT +static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) +{ + int column, page, status, ret = -EIO, chipnr; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Shift to get page */ + page = (int) (to >> this->page_shift); + chipnr = (int) (to >> this->chip_shift); + + /* Mask to get column */ + column = to & (mtd->oobsize - 1); + + /* Initialize return length value */ + *retlen = 0; + + /* Do not allow write past end of page */ + if ((column + len) > mtd->oobsize) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Reset the chip. Some chips (like the Toshiba TC5832DC found + in one of my DiskOnChip 2000 test units) will clear the whole + data page too if we don't do this. I have no clue why, but + I seem to have 'fixed' it in the doc2000 driver in + August 1999. dwmw2. */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == this->pagebuf) + this->pagebuf = -1; + + if (NAND_MUST_PAD(this)) { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask); + /* prepad 0xff for partial programming */ + this->write_buf(mtd, ffchars, column); + /* write data */ + this->write_buf(mtd, buf, len); + /* postpad 0xff for partial programming */ + this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); + } else { + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask); + /* write data */ + this->write_buf(mtd, buf, len); + } + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + ret = -EIO; + goto out; + } + /* Return happy */ + *retlen = len; + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask); + + if (this->verify_buf(mtd, buf, len)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; + } +#endif + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} +#endif + + +/** + * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * + * NAND write with kvec. This just calls the ecc function + */ +#if NAND_KVEC_SUPPORT +#if NAND_WRITE_SUPPORT +static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen) +{ + return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL)); +} +#endif +#endif + +/** + * nand_writev_ecc - [MTD Interface] write with iovec with ecc + * @mtd: MTD device structure + * @vecs: the iovectors to write + * @count: number of vectors + * @to: offset to write to + * @retlen: pointer to variable to store the number of written bytes + * @eccbuf: filesystem supplied oob data buffer + * @oobsel: oob selection structure + * + * NAND write with iovec with ecc + */ +#if NAND_KVEC_SUPPORT +#if NAND_WRITE_SUPPORT +static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, + loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) +{ + int i, page, len, total_len, ret = -EIO, written = 0, chipnr; + int oob, numpages, autoplace = 0, startpage; + struct nand_chip *this = mtd->priv; + int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); + u_char *oobbuf, *bufstart; + + /* Preset written len for early exit */ + *retlen = 0; + + /* Calculate total length of data */ + total_len = 0; + for (i = 0; i < count; i++) + total_len += (int) vecs[i].iov_len; + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); + + /* Do not allow write past end of page */ + if ((to + total_len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); + return -EINVAL; + } + + /* reject writes, which are not page aligned */ + if (NOTALIGNED (to) || NOTALIGNED(total_len)) { + puts ("nand_write_ecc: Attempt to write not page aligned data\r\n"); + return -EINVAL; + } + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_WRITING); + + /* Get the current chip-nr */ + chipnr = (int) (to >> this->chip_shift); + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + goto out; + + /* if oobsel is NULL, use chip defaults */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + /* Autoplace of oob data ? Use the default placement scheme */ + if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { + oobsel = this->autooob; + autoplace = 1; + } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; + + /* Setup start page */ + page = (int) (to >> this->page_shift); + /* Invalidate the page cache, if we write to the cached page */ + if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift)) + this->pagebuf = -1; + + startpage = page & this->pagemask; + + /* Loop until all kvec' data has been written */ + len = 0; + while (count) { + /* If the given tuple is >= pagesize then + * write it out from the iov + */ + if ((vecs->iov_len - len) >= mtd->oobblock) { + /* Calc number of pages we can write + * out of this iov in one go */ + numpages = (vecs->iov_len - len) >> this->page_shift; + /* Do not cross block boundaries */ + numpages = min (ppblock - (startpage & (ppblock - 1)), numpages); + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + bufstart = (u_char *)vecs->iov_base; + bufstart += len; + this->data_poi = bufstart; + oob = 0; + for (i = 1; i <= numpages; i++) { + /* Write one page. If this is the last page to write + * then use the real pageprogram command, else select + * cached programming if supported by the chip. + */ + ret = nand_write_page (mtd, this, page & this->pagemask, + &oobbuf[oob], oobsel, i != numpages); + if (ret) + goto out; + this->data_poi += mtd->oobblock; + len += mtd->oobblock; + oob += mtd->oobsize; + page++; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } else { + /* We must use the internal buffer, read data out of each + * tuple until we have a full page to write + */ + int cnt = 0; + while (cnt < mtd->oobblock) { + if (vecs->iov_base != NULL && vecs->iov_len) + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + this->pagebuf = page; + this->data_poi = this->data_buf; + bufstart = this->data_poi; + numpages = 1; + oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages); + ret = nand_write_page (mtd, this, page & this->pagemask, + oobbuf, oobsel, 0); + if (ret) + goto out; + page++; + } + + this->data_poi = bufstart; + ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0); + if (ret) + goto out; + + written += mtd->oobblock * numpages; + /* All done ? */ + if (!count) + break; + + startpage = page & this->pagemask; + /* Check, if we cross a chip boundary */ + if (!startpage) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + } + } + ret = 0; +out: + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + *retlen = written; + return ret; +} +#endif +#endif + +/** + * single_erease_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips + */ +#if NAND_ERASE_SUPPORT +static void single_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} +#endif + +/** + * multi_erease_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function + * Erase 4 consecutive blocks + */ +#if NAND_ERASE_SUPPORT +static void multi_erase_cmd (struct mtd_info *mtd, int page) +{ + struct nand_chip *this = mtd->priv; + /* Send commands to erase a block */ + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); +} +#endif + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks + */ +#if NAND_ERASE_SUPPORT +static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand (mtd, instr, 0); +} +#endif + +#define BBT_PAGE_MASK 0xffffff3f +/** + * nand_erase_intern - [NAND Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks + */ +#if NAND_ERASE_SUPPORT +int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt) +{ + int page, len, status, pages_per_block, ret, chipnr; + struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ + + DEBUG (MTD_DEBUG_LEVEL3, + "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << this->phys_erase_shift) - 1)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int) (instr->addr >> this->page_shift); + chipnr = (int) (instr->addr >> this->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (this->phys_erase_shift - this->page_shift); + + /* Select the NAND device */ + this->select_chip(mtd, chipnr); + + /* Check the WP bit */ + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ + if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) { + puts ("nand_erase: attempt to erase a bad block at page "); + putx (page); + putnl (); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Invalidate the page cache, if we erase the block which contains + the current cached page */ + if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block)) + this->pagebuf = -1; + + this->erase_cmd (mtd, page & this->pagemask); + + status = this->waitfunc (mtd, this, FL_ERASING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = (page << this->page_shift); + goto erase_exit; + } + + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } + + /* Increment page address and decrement length */ + len -= (1 << this->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & this->pagemask)) { + chipnr++; + this->select_chip(mtd, -1); + this->select_chip(mtd, chipnr); + + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; +#if 0 + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); +#endif + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + +#if NAND_BBT_SUPPORT + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } +#endif + + /* Return more or less happy */ + return ret; +} +#endif + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function + */ +#if 0 /* not needed */ +static void nand_sync (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + nand_get_device (this, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device (mtd); +} +#endif + + +/** + * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs) +{ + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + return nand_block_checkbad (mtd, ofs, 1, 0); +} + +/** + * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +#if NAND_WRITE_SUPPORT +static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *this = mtd->priv; + int ret; + + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return this->block_markbad(mtd, ofs); +} +#endif + +/** + * nand_suspend - [MTD Interface] Suspend the NAND flash + * @mtd: MTD device structure + */ +#if 0 /* don't need it */ +static int nand_suspend(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + return nand_get_device (this, mtd, FL_PM_SUSPENDED); +} +#endif + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd: MTD device structure + */ +#if 0 /* don't need it */ +static void nand_resume(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + if (this->state == FL_PM_SUSPENDED) + nand_release_device(mtd); + else + puts("resume() called for the chip which is not in suspended state\n"); + +} +#endif + + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. Buffers are allocated if + * they are not provided by the board driver + * + */ +int nand_scan (struct mtd_info *mtd, int maxchips) +{ + int i, nand_maf_id, nand_dev_id, busw, maf_id; + struct nand_chip *this = mtd->priv; + + /* Get buswidth to select the correct functions*/ + busw = this->options & NAND_BUSWIDTH_16; + + /* check for proper chip_delay setup, set 20us if not */ + if (!this->chip_delay) + this->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (this->cmdfunc == NULL) + this->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + + if (!this->select_chip) + this->select_chip = nand_select_chip; + if (!this->write_byte) + this->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!this->read_byte) + this->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!this->write_word) + this->write_word = nand_write_word; + if (!this->read_word) + this->read_word = nand_read_word; + if (!this->block_bad) + this->block_bad = nand_block_bad; +#if NAND_WRITE_SUPPORT + if (!this->block_markbad) + this->block_markbad = nand_default_block_markbad; +#endif + if (!this->write_buf) + this->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!this->read_buf) + this->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!this->verify_buf) + this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; +#if NAND_BBT_SUPPORT + if (!this->scan_bbt) + this->scan_bbt = nand_default_bbt; +#endif + + /* Select the device */ + this->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + nand_maf_id = this->read_byte(mtd); + nand_dev_id = this->read_byte(mtd); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + + if (nand_dev_id != nand_flash_ids[i].id) + continue; + + if (!mtd->name) mtd->name = nand_flash_ids[i].name; + this->chipsize = nand_flash_ids[i].chipsize << 20; + + /* New devices have all the information in additional id bytes */ + if (!nand_flash_ids[i].pagesize) { + int extid; + /* The 3rd id byte contains non relevant data ATM */ + extid = this->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = this->read_byte(mtd); + /* Calc pagesize */ + mtd->oobblock = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* Old devices have this data hardcoded in the + * device id table */ + mtd->erasesize = nand_flash_ids[i].erasesize; + mtd->oobblock = nand_flash_ids[i].pagesize; + mtd->oobsize = mtd->oobblock / 32; + busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; + } + + /* Try to identify manufacturer */ + for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { + if (nand_manuf_ids[maf_id].id == nand_maf_id) + break; + } + + /* Check, if buswidth is correct. Hardware drivers should set + * this correct ! */ + if (busw != (this->options & NAND_BUSWIDTH_16)) { +#if 0 + puts ("Manufacturer ID: "); + putx (nand_maf_id); + puts (", Chip ID: "); + putx (nand_dev_id); + puts (" ("); + puts (nand_manuf_ids[maf_id].name); + putc (' '); + puts (mtd->name); + puts (")\r\n"); +#endif + puts ("Expected NAND bus width "); + putx ((this->options & NAND_BUSWIDTH_16) ? 16 : 8); + puts (",found "); + putx (busw ? 16 : 8); + putnl(); + this->select_chip(mtd, -1); + return 1; + } + + /* Calculate the address shift from the page size */ + this->page_shift = ffs(mtd->oobblock) - 1; + this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; + this->chip_shift = ffs(this->chipsize) - 1; + + /* Set the bad block position */ + this->badblockpos = mtd->oobblock > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + this->options &= ~NAND_CHIPOPTIONS_MSK; + this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK; + /* Set this as a default. Board drivers can override it, if neccecary */ + this->options |= NAND_NO_AUTOINCR; + /* Check if this is a not a samsung device. Do not clear the options + * for chips which are not having an extended id. + */ + if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize) + this->options &= ~NAND_SAMSUNG_LP_OPTIONS; + +#if NAND_ERASE_SUPPORT + /* Check for AND chips with 4 page planes */ + if (this->options & NAND_4PAGE_ARRAY) + this->erase_cmd = multi_erase_cmd; + else + this->erase_cmd = single_erase_cmd; +#endif + + /* Do not replace user supplied command function ! */ + if (mtd->oobblock > 512 && this->cmdfunc == nand_command) + this->cmdfunc = nand_command_lp; + + puts ("Manufacturer ID / Chip ID: "); + putx (nand_maf_id << 8 | nand_dev_id); + puts (" ("); + puts (nand_manuf_ids[maf_id].name); + putc (' '); + puts (nand_flash_ids[i].name); + puts (")\r\n"); + break; + } + + if (!nand_flash_ids[i].name) { + puts ("No NAND device found!!!\r\n"); + this->select_chip(mtd, -1); + return 1; + } + +#if NAND_MULTICHIP_SUPPORT + for (i=1; i < maxchips; i++) { + this->select_chip(mtd, i); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + if (nand_maf_id != this->read_byte(mtd) || + nand_dev_id != this->read_byte(mtd)) + break; + } + if (i > 1) { + putx (i); + puts (" NAND chips detected\r\n"); + } +#endif + + /* Allocate buffers, if neccecary */ + if (!this->oob_buf) { + size_t len; + len = mtd->oobsize << (this->phys_erase_shift - this->page_shift); + this->oob_buf = malloc (len); + if (!this->oob_buf) { + puts ("nand_scan(): Cannot allocate oob_buf\r\n"); + return -ENOMEM; + } + this->options |= NAND_OOBBUF_ALLOC; + } + + if (!this->data_buf) { + size_t len; + len = mtd->oobblock + mtd->oobsize; + this->data_buf = malloc (len); + if (!this->data_buf) { + if (this->options & NAND_OOBBUF_ALLOC) + free (this->oob_buf); + puts ("nand_scan(): Cannot allocate data_buf\r\n"); + return -ENOMEM; + } + this->options |= NAND_DATABUF_ALLOC; + } + +#if NAND_MULTICHIP_SUPP0RT + /* Store the number of chips and calc total size for mtd */ + this->numchips = i; + mtd->size = i * this->chipsize; +#else + /* Store the number of chips and calc total size for mtd */ + this->numchips = 1; + mtd->size = this->chipsize; +#endif + + /* Convert chipsize to number of pages per chip -1. */ + this->pagemask = (this->chipsize >> this->page_shift) - 1; + /* Preset the internal oob buffer */ + memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); + + /* If no default placement scheme is given, select an + * appropriate one */ + if (!this->autooob) { + /* Select the appropriate default oob placement scheme for + * placement agnostic filesystems */ + switch (mtd->oobsize) { + case 8: + this->autooob = &nand_oob_8; + break; + case 16: + this->autooob = &nand_oob_16; + break; + case 64: + this->autooob = &nand_oob_64; + break; + default: + puts ("No oob scheme defined for oobsize "); + putx (mtd->oobsize); + putnl (); + BUG(); + } + } + + /* The number of bytes available for the filesystem to place fs dependend + * oob data */ + mtd->oobavail = 0; + for (i = 0; this->autooob->oobfree[i][1]; i++) + mtd->oobavail += this->autooob->oobfree[i][1]; + + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize + * fallback to software ECC + */ + this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; + + switch (this->eccmode) { +#if NAND_HWECC_SUPPORT + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + puts ("2048 byte HW ECC not possible on "); + putx (mtd->oobblock); + puts (" byte page size, fallback to SW ECC\r\n"); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + if (mtd->oobblock == 256) { + puts ("512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC\r\n"); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 512; /* set eccsize to 512 */ + break; + + case NAND_ECC_HW3_256: + break; +#endif + + case NAND_ECC_NONE: + puts ("NAND_ECC_NONE selected by board driver. This is not recommended !!\r\n"); + this->eccmode = NAND_ECC_NONE; + break; + + case NAND_ECC_SOFT: + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + + default: + puts ("Invalid NAND_ECC_MODE "); + putx (this->eccmode); + putnl (); + BUG(); + } + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + puts ("No ECC functions supplied, Hardware ECC not possible\r\n"); + BUG(); + } + + mtd->eccsize = this->eccsize; + + /* Set the number of read / write steps for one page to ensure ECC generation */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: + case NAND_ECC_HW8_512: + this->eccsteps = mtd->oobblock / 512; + break; + case NAND_ECC_HW3_256: + case NAND_ECC_SOFT: + this->eccsteps = mtd->oobblock / 256; + break; + + case NAND_ECC_NONE: + this->eccsteps = 1; + break; + } + + /* Initialize state, waitqueue and spinlock */ + this->state = FL_READY; +#if 0 + init_waitqueue_head (&this->wq); + spin_lock_init (&this->chip_lock); +#endif + + /* De-select the device */ + this->select_chip(mtd, -1); + + /* Invalidate the pagebuffer reference */ + this->pagebuf = -1; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; + mtd->ecctype = MTD_ECC_SW; +#if NAND_ERASE_SUPPORT + mtd->erase = nand_erase; +#endif +#if 0 /* not needed */ + mtd->point = NULL; + mtd->unpoint = NULL; +#endif + mtd->read = nand_read; +#if NAND_WRITE_SUPPORT + mtd->write = nand_write; +#endif + mtd->read_ecc = nand_read_ecc; +#if NAND_WRITE_SUPPORT + mtd->write_ecc = nand_write_ecc; +#endif + mtd->read_oob = nand_read_oob; +#if NAND_WRITE_SUPPORT + mtd->write_oob = nand_write_oob; +#endif +#if NAND_KVEC_SUPPORT + mtd->readv = NULL; +#endif +#if NAND_WRITE_SUPPORT && NAND_KVEC_SUPPORT + mtd->writev = nand_writev; + mtd->writev_ecc = nand_writev_ecc; +#endif +#if 0 /* not needed */ + mtd->sync = nand_sync; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = nand_suspend; + mtd->resume = nand_resume; +#endif + mtd->block_isbad = nand_block_isbad; +#if NAND_WRITE_SUPPORT + mtd->block_markbad = nand_block_markbad; +#endif + + /* and make the autooob the default one */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + +#ifdef THIS_MODULE /* normally isn't for us */ + mtd->owner = THIS_MODULE; +#endif + + /* Check, if we should skip the bad block table scan */ + if (this->options & NAND_SKIP_BBTSCAN) + return 0; + +#if NAND_BBT_SUPPORT + /* Build bad block table */ + return this->scan_bbt (mtd); +#else + return -1; +#endif +} + +/** + * nand_release - [NAND Interface] Free resources held by the NAND device + * @mtd: MTD device structure +*/ +#if 0 /* don't need it */ +void nand_release (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + +#if 0 +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions (mtd); +#endif + /* Deregister the device */ + del_mtd_device (mtd); +#endif + + /* Free bad block table memory */ + free (this->bbt); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_OOBBUF_ALLOC) + free (this->oob_buf); + /* Buffer allocated by nand_scan ? */ + if (this->options & NAND_DATABUF_ALLOC) + free (this->data_buf); +} +#endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_bbt.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_bbt.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_bbt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_bbt.c 2006-11-10 09:55:58.000000000 +0100 @@ -0,0 +1,1151 @@ +/* + * drivers/mtd/nand_bbt.c + * + * Overview: + * Bad block table support for the NAND driver + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_bbt.c,v 1.5 2006/11/10 08:55:58 ricardw Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the bbt descriptor(s). If a bbt is found + * then the contents are read and the memory based bbt is created. If a + * mirrored bbt is selected then the mirror is searched too and the + * versions are compared. If the mirror has a greater version number + * than the mirror bbt is used to build the memory based bbt. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the bbt's is out of date or does not exist it is (re)created. + * If no bbt exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created bbts like the one found on M-SYS DOC devices + * the bbt is searched and read but never created + * + * The autogenerated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the oob area with an ident pattern and a version + * number which indicates which of both tables is more up to date. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space neccecary for a bbt in FLASH does not exceed a block boundary + * + */ + +#if 0 +#include +#endif + +#include +#include "mtd.h" +#include "nand.h" +#include "nand_ecc.h" + +#if 0 +#include +#include +#endif + +#include + +#include "lib.h" + + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. + * If the SCAN_EMPTY option is set then check, if all bytes except the + * pattern area contain 0xff + * +*/ +static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i, end = 0; + uint8_t *p = buf; + + end = paglen + td->offs; + if (td->options & NAND_BBT_SCANEMPTY) { + for (i = 0; i < end; i++) { + if (p[i] != 0xff) + return -1; + } + } + p += end; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; + for (i = end; i < len; i++) { + if (*p++ != 0xff) + return -1; + } + } + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check + * +*/ +static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[td->offs + i] != td->pattern[i]) + return -1; + } + return 0; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @bits: number of bits per block + * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks + * + * Read the bad block table starting from page. + * + */ +static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, + int bits, int offs, int reserved_block_code) +{ + int res, i, j, act = 0; + struct nand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + uint8_t msk = (uint8_t) ((1 << bits) - 1); + + totlen = (num * bits) >> 3; + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); + res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); + if (res < 0) { + if (retlen != len) { + puts ("nand_bbt: Error reading bad block table\n"); + return res; + } + puts ("nand_bbt: ECC error while reading bad block table\n"); + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && + (tmp == reserved_block_code)) { + puts ("nand_read_bbt: Reserved block at"); + putx (((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + putnl (); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + continue; + } + /* Leave it for now, if its matured we can move this + * message to MTD_DEBUG_LEVEL0 */ + puts ("nand_read_bbt: Bad block at "); + putx (((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + putnl (); + /* Factory marked bad or worn out ? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + } + } + totlen -= len; + from += len; + } + return 0; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. +*/ +static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd->priv; + int res = 0, i; + int bits; + + bits = td->options & NAND_BBT_NRBITS_MSK; + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + if (res) + return res; + offs += this->chipsize >> (this->bbt_erase_shift + 2); + } + } else { + res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); + if (res) + return res; + } + return 0; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + * +*/ +static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd->priv; + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + td->version[0] = buf[mtd->oobblock + td->veroffs]; + puts ("Bad block table at page "); + putx (td->pages[0]); + puts (", version "); + putx (td->version[0]); + putnl (); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); + md->version[0] = buf[mtd->oobblock + md->veroffs]; + puts ("Bad block table at page "); + putx (md->pages[0]); + puts (", version "); + putx (md->version[0]); + putnl (); + } + + return 1; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd->priv; + int i, j, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen, ooblen; + + puts ("Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCANALLPAGES) + len = 1 << (this->bbt_erase_shift - this->page_shift); + else { + if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; + } + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + } + + if (chip == -1) { + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it + * makes shifting and masking less painful */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + puts ("create_bbt(): chipnr ("); + putx (chip + 1); + puts (" > available chips "); + putx (this->numchips); + putnl (); + return -EINVAL; + } + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = startblock << (this->bbt_erase_shift - 1); + } + + for (i = startblock; i < numblocks;) { + int ret; + + if (bd->options & NAND_BBT_SCANEMPTY) + if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) + return ret; + + for (j = 0; j < len; j++) { + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + size_t retlen; + + /* Read the full oob until read_oob is fixed to + * handle single byte reads for 16 bit buswidth */ + ret = mtd->read_oob(mtd, from + j * mtd->oobblock, + mtd->oobsize, &retlen, buf); + if (ret) + return ret; + + if (check_short_pattern (buf, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + puts ("Bad eraseblock "); + putx (i >> 1); + puts (" at "); + putx ((unsigned int) from); + putnl (); + break; + } + } else { + if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + puts ("Bad eraseblock "); + putx (i >> 1); + puts (" at "); + putx ((unsigned int) from); + putnl (); + break; + } + } + } + i += 2; + from += (1 << this->bbt_erase_shift); + } + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. + * Search is preformed either from the beginning up or from the end of + * the device downwards. The search starts always at the start of a + * block. + * If the option NAND_BBT_PERCHIP is given, each chip is searched + * for a bbt, which contains the bad block information of this chip. + * This is neccecary to provide support for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page + * in a block. + */ +static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, chips; + int bits, startblock, block, dir; + int scanlen = mtd->oobblock + mtd->oobsize; + int bbtblocks; + + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) -1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + int actblock = startblock + dir * block; + /* Read first page */ + nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); + if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { + td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); + if (td->options & NAND_BBT_VERSION) { + td->version[i] = buf[mtd->oobblock + td->veroffs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) { + puts ("Bad block table not found for chip "); + putx (i); + putnl (); + } else { + puts ("Bad block table found at page "); + putx (td->pages[i]); + puts (" version "); + putx (td->version[i]); + putnl (); + } + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s) +*/ +static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt (mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt (mtd, buf, md); + + /* Force result check */ + return 1; +} + + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table + * +*/ +static int write_bbt (struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) +{ + struct nand_chip *this = mtd->priv; + struct nand_oobinfo oobinfo; + struct erase_info einfo; + int i, j, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, bbtoffs, pageoffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device ? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int) (this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip ? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int) (mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + + /* There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* Automatic placement of the bad block table */ + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + puts ("No space left to write bad block table\r\n"); + return -ENOSPC; +write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; + default: return -EINVAL; + } + + bbtoffs = chip * (numblocks >> 2); + + to = ((loff_t) page) << this->page_shift; + + memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); + oobinfo.useecc = MTD_NANDECC_PLACEONLY; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + if (retlen != len) { + puts ("nand_bbt: Error reading block for writing the bad block table\r\n"); + return res; + } + puts ("nand_bbt: ECC error while reading block for writing bad block table\r\n"); + } + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); + /* Preset the bbt's oob area with 0xff */ + memset (&buf[len + pageoffs * mtd->oobsize], 0xff, + ((len >> this->page_shift) - pageoffs) * mtd->oobsize); + if (td->options & NAND_BBT_VERSION) { + buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; + } + } else { + /* Calc length */ + len = (size_t) (numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); + /* Preset the buffer with 0xff */ + memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); + offs = 0; + /* Pattern is located in oob area of first page */ + memcpy (&buf[len + td->offs], td->pattern, td->len); + if (td->options & NAND_BBT_VERSION) { + buf[len + td->veroffs] = td->version[chip]; + } + } + + /* walk through the memory table */ + for (i = 0; i < numblocks; ) { + uint8_t dat; + dat = this->bbt[bbtoffs + (i >> 2)]; + for (j = 0; j < 4; j++ , i++) { + int sftcnt = (i << (3 - sft)) & sftmsk; + /* Do not store the reserved bbt blocks ! */ + buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset (&einfo, 0, sizeof (einfo)); + einfo.mtd = mtd; + einfo.addr = (unsigned long) to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand (mtd, &einfo, 1); + if (res < 0) { + puts ("nand_bbt: Error during block erase: "); + putx (res); + putnl(); + return res; + } + + res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); + if (res < 0) { + puts ("nand_bbt: Error while writing bad block table "); + putx (res); + putnl (); + return res; + } + puts ("Bad block table written to "); + putx ((unsigned int) to); + puts (", version "); + putx (td->version[chip]); + putnl (); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks +*/ +static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt (mtd, this->data_buf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if neccecary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt + * and creates / updates the bbt(s) if neccecary + * Creation is neccecary if no bbt was found for the chip/device + * Update is neccecary if one of the tables is missing or the + * version nr. of one table is less than the other +*/ +static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, chipsel, res; + struct nand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + rd = NULL; + rd2 = NULL; + /* Per chip or per device ? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table avilable ? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + writeops = 0x03; + goto create; + } + + if (td->pages[i] == -1) { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + goto writecheck; + } + + if (md->pages[i] == -1) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + goto writecheck; + } + + if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + goto writecheck; + } + + if (((int8_t) (td->version[i] - md->version[i])) > 0) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + } else { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + } + + goto writecheck; + + } else { + if (td->pages[i] == -1) { + writeops = 0x01; + goto create; + } + rd = td; + goto writecheck; + } +create: + /* Create the bad block table by scanning the device ? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + create_bbt (mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; +writecheck: + /* read back first ? */ + if (rd) + read_abs_bbt (mtd, buf, rd, chipsel); + /* If they weren't versioned, read both. */ + if (rd2) + read_abs_bbt (mtd, buf, rd2, chipsel); + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. +*/ +static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, j, chips, block, nrblocks, update; + uint8_t oldval, newval; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) update = 1; + block += 2; + } + /* If we want reserved blocks to be recorded to flash, and some + new ones have been marked, then we need to update the stored + bbts. This should only happen once. */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + } +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * +*/ +int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate memory (2bit per block) */ + this->bbt = malloc (len); + if (!this->bbt) { + puts ("nand_scan_bbt: Out of memory\r\n"); + return -ENOMEM; + } + /* Clear the memory bad block table */ + memset (this->bbt, 0x00, len); + + /* If no primary table decriptor is given, scan the device + * to build a memory based bad block table + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + puts ("nand_bbt: Can't scan flash and build the RAM-based BBT\r\n"); + free (this->bbt); + this->bbt = NULL; + } + return res; + } + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = malloc (len); + if (!buf) { + puts ("nand_bbt: Out of memory\r\n"); + free (this->bbt); + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page ? */ + if (td->options & NAND_BBT_ABSPAGE) { + res = read_abs_bbts (mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + res = search_read_bbts (mtd, buf, td, md); + } + + if (res) + res = check_create (mtd, buf, bd); + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region (mtd, td); + if (md) + mark_bbt_region (mtd, md); + + free (buf); + return res; +} + + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s) +*/ +int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = malloc (len); + if (!buf) { + puts ("nand_update_bbt: Out of memory\r\n"); + return -ENOMEM; + } + + writeops = md != NULL ? 0x03 : 0x01; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int) (offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt (mtd, buf, md, td, chipsel); + } + +out: + free (buf); + return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr smallpage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the nand_scan_bbt function + * +*/ +int nand_default_bbt (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Default for AG-AND. We must use a flash based + * bad block table as the devices have factory marked + * _good_ blocks. Erasing those blocks leads to loss + * of the good / bad information, so we _must_ store + * this information in a good / bad table during + * startup + */ + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + this->options |= NAND_USE_FLASH_BBT; + return nand_scan_bbt (mtd, &agand_flashbased); + } + + + /* Is a flash based bad block table requested ? */ + if (this->options & NAND_USE_FLASH_BBT) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + } + return nand_scan_bbt (mtd, this->badblock_pattern); +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + * +*/ +int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int) (offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block >> 1, res); + + switch ((int)res) { + case 0x00: return 0; + case 0x01: return 1; + case 0x02: return allowbbt ? 0 : 1; + } + return 1; +} diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.c 2006-08-04 16:38:14.000000000 +0200 @@ -0,0 +1,246 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * Taken from drivers/mtd/nand/nand_ecc.c, modified for + * NAND flash boot on Etrax FS. + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * $Id: nand_ecc.c,v 1.1 2006/08/04 14:38:14 ricardw Exp $ + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include +#if 0 +#include +#include +#endif +#include "nand_ecc.h" + +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const u_char nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + + +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2: line parity reg 2 + * @reg3: line parity reg 3 + * @ecc_code: ecc + * + * Creates non-inverted ECC code from line parity + */ +static void nand_trans_result(u_char reg2, u_char reg3, + u_char *ecc_code) +{ + u_char a, b, i, tmp1, tmp2; + + /* Initialize variables */ + a = b = 0x80; + tmp1 = tmp2 = 0; + + /* Calculate first ECC byte */ + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */ + tmp1 |= b; + b >>= 1; + a >>= 1; + } + + /* Calculate second ECC byte */ + b = 0x80; + for (i = 0; i < 4; i++) { + if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */ + tmp2 |= b; + b >>= 1; + a >>= 1; + } + + /* Store two of the ECC bytes */ + ecc_code[0] = tmp1; + ecc_code[1] = tmp2; +} + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + u_char idx, reg1, reg2, reg3; + int j; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + + /* Build up column parity */ + for(j = 0; j < 256; j++) { + + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[dat[j]]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (u_char) j; + reg2 ^= ~((u_char) j); + } + } + + /* Create non-inverted ECC code from line parity */ + nand_trans_result(reg2, reg3, ecc_code); + + /* Calculate final ECC code */ + ecc_code[0] = ~ecc_code[0]; + ecc_code[1] = ~ecc_code[1]; + ecc_code[2] = ((~reg1) << 2) | 0x03; + return 0; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + u_char a, b, c, d1, d2, d3, add, bit, i; + + /* Do error detection */ + d1 = calc_ecc[0] ^ read_ecc[0]; + d2 = calc_ecc[1] ^ read_ecc[1]; + d3 = calc_ecc[2] ^ read_ecc[2]; + + if ((d1 | d2 | d3) == 0) { + /* No errors */ + return 0; + } + else { + a = (d1 ^ (d1 >> 1)) & 0x55; + b = (d2 ^ (d2 >> 1)) & 0x55; + c = (d3 ^ (d3 >> 1)) & 0x54; + + /* Found and will correct single bit error in the data */ + if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { + c = 0x80; + add = 0; + a = 0x80; + for (i=0; i<4; i++) { + if (d1 & c) + add |= a; + c >>= 2; + a >>= 1; + } + c = 0x80; + for (i=0; i<4; i++) { + if (d2 & c) + add |= a; + c >>= 2; + a >>= 1; + } + bit = 0; + b = 0x04; + c = 0x80; + for (i=0; i<3; i++) { + if (d3 & c) + bit |= b; + c >>= 2; + b >>= 1; + } + b = 0x01; + a = dat[add]; + a ^= (b << bit); + dat[add] = a; + return 1; + } + else { + i = 0; + while (d1) { + if (d1 & 0x01) + ++i; + d1 >>= 1; + } + while (d2) { + if (d2 & 0x01) + ++i; + d2 >>= 1; + } + while (d3) { + if (d3 & 0x01) + ++i; + d3 >>= 1; + } + if (i == 1) { + /* ECC Code Error Correction */ + read_ecc[0] = calc_ecc[0]; + read_ecc[1] = calc_ecc[1]; + read_ecc[2] = calc_ecc[2]; + return 2; + } + else { + /* Uncorrectable Error */ + return -1; + } + } + } + + /* Should never happen */ + return -1; +} diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.h --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.h 2006-08-04 16:38:14.000000000 +0200 @@ -0,0 +1,30 @@ +/* + * drivers/mtd/nand_ecc.h + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: nand_ecc.h,v 1.1 2006/08/04 14:38:14 ricardw Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file is the header for the ECC algorithm. + */ + +#ifndef __MTD_NAND_ECC_H__ +#define __MTD_NAND_ECC_H__ + +struct mtd_info; + +/* + * Calculate 3 byte ECC code for 256 byte block + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + +/* + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + +#endif /* __MTD_NAND_ECC_H__ */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ids.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ids.c --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ids.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ids.c 2006-08-04 16:38:14.000000000 +0200 @@ -0,0 +1,133 @@ +/* + * drivers/mtd/nandids.c + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_ids.c,v 1.1 2006/08/04 14:38:14 ricardw Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#if 0 +#include +#endif + +#include "nand.h" +/* +* Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size +*/ +struct nand_flash_dev nand_flash_ids[] = { + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + /* These are the new chips with large page size. The pagesize + * and the erasesize is determined from the extended id bytes + */ + /*512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + + /* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! + * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes + * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 + * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go + * There are more speed improvements for reads and writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, + + {NULL,} +}; + +/* +* Manufacturer ID list +*/ +struct nand_manufacturers nand_manuf_ids[] = { + {NAND_MFR_TOSHIBA, "Toshiba"}, + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, + {0x0, "Unknown"} +}; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/rescue.ld linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/rescue.ld --- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/rescue.ld 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/rescue.ld 2006-10-18 10:52:47.000000000 +0200 @@ -1,20 +1,40 @@ -MEMORY +/*#OUTPUT_FORMAT(elf32-us-cris) */ +OUTPUT_ARCH (crisv32) + +MEMORY { - flash : ORIGIN = 0x00000000, - LENGTH = 0x00100000 + bootblk : ORIGIN = 0x38000000, + LENGTH = 0x00004000 + intmem : ORIGIN = 0x38004000, + LENGTH = 0x00005000 } SECTIONS { .text : { - stext = . ; + _stext = . ; *(.text) - etext = . ; - } > flash + *(.rodata) + *(.rodata.*) + _etext = . ; + } > bootblk .data : { *(.data) - edata = . ; - } > flash + _edata = . ; + } > bootblk + .bss : + { + _bss = . ; + *(.bss) + _end = ALIGN( 0x10 ) ; + } > intmem + + /* Get rid of stuff from EXPORT_SYMBOL(foo). */ + /DISCARD/ : + { + *(__ksymtab_strings) + *(__ksymtab) + } } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Kconfig --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Kconfig 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Kconfig 2007-01-29 16:14:16.000000000 +0100 @@ -6,14 +6,6 @@ This option enables the ETRAX FS built-in 10/100Mbit Ethernet controller. -config ETRAX_ETHERNET_HW_CSUM - bool "Hardware accelerated ethernet checksum and scatter/gather" - depends on ETRAX_ETHERNET - depends on ETRAX_STREAMCOPROC - default y - help - Hardware acceleration of checksumming and scatter/gather - config ETRAX_ETHERNET_IFACE0 depends on ETRAX_ETHERNET bool "Enable network interface 0" @@ -23,6 +15,52 @@ bool "Enable network interface 1 (uses DMA6 and DMA7)" choice + prompt "Eth0 led group" + depends on ETRAX_ETHERNET_IFACE0 + default ETRAX_ETH0_USE_LEDGRP0 + +config ETRAX_ETH0_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 0 for eth0 + +config ETRAX_ETH0_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth0 + +config ETRAX_ETH0_USE_LEDGRPNULL + bool "Use no LEDs for eth0" + help + Use no LEDs for eth0 +endchoice + +choice + prompt "Eth1 led group" + depends on ETRAX_ETHERNET_IFACE1 + default ETRAX_ETH1_USE_LEDGRP1 + +config ETRAX_ETH1_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 0 for eth1 + +config ETRAX_ETH1_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth1 + +config ETRAX_ETH1_USE_LEDGRPNULL + bool "Use no LEDs for eth1" + help + Use no LEDs for eth1 +endchoice + +choice prompt "Network LED behavior" depends on ETRAX_ETHERNET default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY @@ -56,10 +94,25 @@ config ETRAXFS_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V32 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE help Enables the ETRAX FS serial driver for ser0 (ttyS0) You probably want this enabled. +config ETRAX_RS485 + bool "RS-485 support" + depends on ETRAXFS_SERIAL + help + Enables support for RS-485 serial communication. + +config ETRAX_RS485_DISABLE_RECEIVER + bool "Disable serial receiver" + depends on ETRAX_RS485 + help + It is necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + config ETRAX_SERIAL_PORT0 bool "Serial port 0 enabled" depends on ETRAXFS_SERIAL @@ -70,6 +123,31 @@ ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. choice + prompt "Ser0 default port type " + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_TYPE_232 + help + Type of serial port. + +config ETRAX_SERIAL_PORT0_TYPE_232 + bool "Ser0 is a RS-232 port" + help + Configure serial port 0 to be a RS-232 port. + +config ETRAX_SERIAL_PORT0_TYPE_485HD + bool "Ser0 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 0 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT0_TYPE_485FD + bool "Ser0 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 0 to be a full duplex (four wires) RS-485 port. +endchoice + +choice prompt "Ser0 DMA in channel " depends on ETRAX_SERIAL_PORT0 default ETRAX_SERIAL_PORT0_NO_DMA_IN @@ -139,6 +217,31 @@ Enables the ETRAX FS serial driver for ser1 (ttyS1). choice + prompt "Ser1 default port type" + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_TYPE_232 + help + Type of serial port. + +config ETRAX_SERIAL_PORT1_TYPE_232 + bool "Ser1 is a RS-232 port" + help + Configure serial port 1 to be a RS-232 port. + +config ETRAX_SERIAL_PORT1_TYPE_485HD + bool "Ser1 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 1 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT1_TYPE_485FD + bool "Ser1 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 1 to be a full duplex (four wires) RS-485 port. +endchoice + +choice prompt "Ser1 DMA in channel " depends on ETRAX_SERIAL_PORT1 default ETRAX_SERIAL_PORT1_NO_DMA_IN @@ -210,6 +313,31 @@ Enables the ETRAX FS serial driver for ser2 (ttyS2). choice + prompt "Ser2 default port type" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_TYPE_232 + help + What DMA channel to use for ser2 + +config ETRAX_SERIAL_PORT2_TYPE_232 + bool "Ser2 is a RS-232 port" + help + Configure serial port 2 to be a RS-232 port. + +config ETRAX_SERIAL_PORT2_TYPE_485HD + bool "Ser2 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 2 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT2_TYPE_485FD + bool "Ser2 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 2 to be a full duplex (four wires) RS-485 port. +endchoice + +choice prompt "Ser2 DMA in channel " depends on ETRAX_SERIAL_PORT2 default ETRAX_SERIAL_PORT2_NO_DMA_IN @@ -279,6 +407,31 @@ Enables the ETRAX FS serial driver for ser3 (ttyS3). choice + prompt "Ser3 default port type" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_TYPE_232 + help + What DMA channel to use for ser3. + +config ETRAX_SERIAL_PORT3_TYPE_232 + bool "Ser3 is a RS-232 port" + help + Configure serial port 3 to be a RS-232 port. + +config ETRAX_SERIAL_PORT3_TYPE_485HD + bool "Ser3 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 3 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT3_TYPE_485FD + bool "Ser3 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 3 to be a full duplex (four wires) RS-485 port. +endchoice + +choice prompt "Ser3 DMA in channel " depends on ETRAX_SERIAL_PORT3 default ETRAX_SERIAL_PORT3_NO_DMA_IN @@ -341,38 +494,6 @@ string "Ser 3 CD bit (empty = not used)" depends on ETRAX_SERIAL_PORT3 -config ETRAX_RS485 - bool "RS-485 support" - depends on ETRAX_SERIAL - help - Enables support for RS-485 serial communication. For a primer on - RS-485, see . - -config ETRAX_RS485_DISABLE_RECEIVER - bool "Disable serial receiver" - depends on ETRAX_RS485 - help - It is necessary to disable the serial receiver to avoid serial - loopback. Not all products are able to do this in software only. - Axis 2400/2401 must disable receiver. - -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V32 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_OBSOLETE_CHIPS - select MTD_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. - config ETRAX_SYNCHRONOUS_SERIAL bool "Synchronous serial-port support" depends on ETRAX_ARCH_V32 @@ -405,6 +526,31 @@ A synchronous serial port can run in manual or DMA mode. Selecting this option will make it run in DMA mode. +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V32 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_AXISFLASHMAP_MTD0WHOLE + bool "MTD0 is whole boot flash device" + depends on ETRAX_AXISFLASHMAP + default N + help + When this option is not set, mtd0 refers to the first partition + on the boot flash device. When set, mtd0 refers to the whole + device, with mtd1 referring to the first partition etc. + config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -425,11 +571,19 @@ This option enables MTD mapping of NAND flash devices. Needed to use NAND flash memories. If unsure, say Y. +config ETRAX_NANDBOOT + bool "Boot from NAND flash" + depends on ETRAX_NANDFLASH + help + This options enables booting from NAND flash devices. + Say Y if your boot code, kernel and root file system is in + NAND flash. Say N if they are in NOR flash. + config ETRAX_I2C bool "I2C driver" depends on ETRAX_ARCH_V32 help - This option enabled the I2C driver used by e.g. the RTC driver. + This option enables the I2C driver used by e.g. the RTC driver. config ETRAX_I2C_DATA_PORT string "I2C data pin" @@ -476,18 +630,19 @@ Remember that you need to setup the port directions appropriately in the General configuration. -config ETRAX_PA_BUTTON_BITMASK - hex "PA-buttons bitmask" +config ETRAX_VIRTUAL_GPIO + bool "Virtual GPIO support" depends on ETRAX_GPIO - default "0x02" help - This is a bitmask (8 bits) with information about what bits on PA - that are used for buttons. - Most products has a so called TEST button on PA1, if that is true - use 0x02 here. - Use 00 if there are no buttons on PA. - If the bitmask is <> 00 a button driver will be included in the gpio - driver. ETRAX general I/O support must be enabled. + Enables the virtual Etrax general port device (major 120, minor 6). + It uses an I/O expander for the I2C-bus. + +config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN + int "Virtual GPIO interrupt pin on PA pin" + range 0 7 + depends on ETRAX_VIRTUAL_GPIO + help + The pin to use on PA for virtual gpio interrupt. config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" @@ -584,6 +739,25 @@ that a user can change the value on using ioctl's. Bit set = changeable. +config ETRAX_PV_CHANGEABLE_DIR + hex "PV user changeable dir mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x0000 here, but it depends on your hardware. + +config ETRAX_PV_CHANGEABLE_BITS + hex "PV user changeable bits mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change the value on using ioctl's. + Bit set = changeable. + config ETRAX_IDE bool "ATA/IDE support" depends on ETRAX_ARCH_V32 @@ -603,11 +777,11 @@ select HOTPLUG select PCCARD_NONSTATIC help - Enabled the ETRAX Carbus driver. + Enabled the ETRAX Carbus driver. config PCI bool - depends on ETRAX_CARDBUS + depends on ETRAX_CARDBUS default y config ETRAX_IOP_FW_LOAD @@ -623,3 +797,175 @@ help This option enables a driver for the stream co-processor for cryptographic operations. + +source drivers/mmc/Kconfig + +config ETRAX_SPI_MMC +# Make this one of several "choices" (possible simultaneously but +# suggested uniquely) when an IOP driver emerges for "real" MMC/SD +# protocol support. + tristate + depends on MMC + default MMC + select SPI + select MMC_SPI + select ETRAX_SPI_MMC_BOARD + +# For the parts that can't be a module (due to restrictions in +# framework elsewhere). +config ETRAX_SPI_MMC_BOARD + boolean + default n + +# While the board info is MMC_SPI only, the drivers are written to be +# independent of MMC_SPI, so we'll keep SPI non-dependent on the +# MMC_SPI config choices (well, except for a single depends-on-line +# for the board-info file until a separate non-MMC SPI board file +# emerges). +# FIXME: When that happens, we'll need to be able to ask for and +# configure non-MMC SPI ports together with MMC_SPI ports (if multiple +# SPI ports are enabled). + +config ETRAX_SPI_SSER0 + tristate "SPI using synchronous serial port 0 (sser0)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m + default y if MMC_SPI=y + default y if MMC_SPI=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 0, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER0_DMA + bool "DMA for SPI on sser0 enabled" + depends on ETRAX_SPI_SSER0 + depends on !ETRAX_SERIAL_PORT1_DMA4_OUT && !ETRAX_SERIAL_PORT1_DMA5_IN + default y + help + Say Y if using DMA (dma4/dma5) for SPI on synchronous serial port 0. + +config ETRAX_SPI_MMC_CD_SSER0_PIN + string "MMC/SD card detect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER0_PIN + string "MMC/SD card write-protect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_SSER1 + tristate "SPI using synchronous serial port 1 (sser1)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 1, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER1_DMA + bool "DMA for SPI on sser1 enabled" + depends on ETRAX_SPI_SSER1 && !ETRAX_ETHERNET_IFACE1 + depends on !ETRAX_SERIAL_PORT0_DMA6_OUT && !ETRAX_SERIAL_PORT0_DMA7_IN + default y + help + Say Y if using DMA (dma6/dma7) for SPI on synchronous serial port 1. + +config ETRAX_SPI_MMC_CD_SSER1_PIN + string "MMC/SD card detect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd12" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER1_PIN + string "MMC/SD card write-protect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd9" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_GPIO + tristate "Bitbanged SPI using gpio pins" + depends on ETRAX_SPI_MMC + select SPI_ETRAX_GPIO + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + help + Say Y for an MMC/SD socket connected to general I/O pins (but not + a complete synchronous serial ports), or for devices using the SPI + protocol on general I/O pins. Slow and slows down the system. + Say m to build it as a module, which will be called spi_crisv32_gpio. + (You need to select MMC separately.) + +# The default match that of sser0, only because that's how it was tested. +config ETRAX_SPI_CS_PIN + string "SPI chip select pin" + depends on ETRAX_SPI_GPIO + default "pc3" + help + The pin to use for SPI chip select. + +config ETRAX_SPI_CLK_PIN + string "SPI clock pin" + depends on ETRAX_SPI_GPIO + default "pc1" + help + The pin to use for the SPI clock. + +config ETRAX_SPI_DATAIN_PIN + string "SPI MISO (data in) pin" + depends on ETRAX_SPI_GPIO + default "pc16" + help + The pin to use for SPI data in from the device. + +config ETRAX_SPI_DATAOUT_PIN + string "SPI MOSI (data out) pin" + depends on ETRAX_SPI_GPIO + default "pc0" + help + The pin to use for SPI data out to the device. + +config ETRAX_SPI_MMC_CD_GPIO_PIN + string "MMC/SD card detect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_GPIO_PIN + string "MMC/SD card write-protect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +# Avoid choices causing non-working configs by conditionalizing the inclusion. +if ETRAX_SPI_MMC +source drivers/spi/Kconfig +endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Makefile 2007-01-29 16:14:16.000000000 +0100 @@ -11,3 +11,4 @@ obj-$(CONFIG_ETRAX_I2C) += i2c.o obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o obj-$(CONFIG_PCI) += pci/ +obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/axisflashmap.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/axisflashmap.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/axisflashmap.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/axisflashmap.c 2007-02-06 17:37:50.000000000 +0100 @@ -11,7 +11,7 @@ * partition split defined below. * * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5 - * with minor changes. + * with quite a few changes now. * */ @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -37,16 +39,24 @@ #define FLASH_UNCACHED_ADDR KSEG_E #define FLASH_CACHED_ADDR KSEG_F +#define PAGESIZE (512) + #if CONFIG_ETRAX_FLASH_BUSWIDTH==1 #define flash_data __u8 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 #define flash_data __u16 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 -#define flash_data __u16 +#define flash_data __u32 #endif /* From head.S */ -extern unsigned long romfs_start, romfs_length, romfs_in_flash; +extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */ +extern unsigned long romfs_start, romfs_length; +extern unsigned long nand_boot; /* 1 when booted from nand flash */ + +struct partition_name { + char name[6]; +}; /* The master mtd for the entire flash. */ struct mtd_info* axisflash_mtd = NULL; @@ -112,32 +122,20 @@ .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE }; -/* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 +#define MAX_PARTITIONS 7 +#ifdef CONFIG_ETRAX_NANDBOOT +#define NUM_DEFAULT_PARTITIONS 4 +#define DEFAULT_ROOTFS_PARTITION_NO 2 +#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */ +#else +#define NUM_DEFAULT_PARTITIONS 3 +#define DEFAULT_ROOTFS_PARTITION_NO (-1) +#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */ +#endif -/* - * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the - * size of one flash block and "filesystem"-partition needs 5 blocks to be able - * to use JFFS. - */ -static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { - { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0 - }, - { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR - }, - { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) - } -}; +#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS) +#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS +#endif /* Initialize the ones normally used. */ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { @@ -178,6 +176,56 @@ }, }; + +/* If no partition-table was found, we use this default-set. + * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most + * likely the size of one flash block and "filesystem"-partition needs + * to be >=5 blocks to be able to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, +#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR) +#ifdef CONFIG_ETRAX_NANDBOOT + { + .name = "rootfs", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = FILESYSTEM_SECTOR + }, +#undef FILESYSTEM_SECTOR +#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR) +#endif + { + .name = "rwfs", + .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR, + .offset = FILESYSTEM_SECTOR + } +}; + +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + +/* Auxilliary partition if we find another flash */ +static struct mtd_partition aux_partition = { + .name = "aux", + .size = 0, + .offset = 0 +}; + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -186,23 +234,23 @@ { struct mtd_info *mtd_cs = NULL; - printk(KERN_INFO + printk(KERN_INFO "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", map_cs->name, map_cs->size, map_cs->map_priv_1); -#ifdef CONFIG_MTD_AMDSTD - mtd_cs = do_map_probe("amd_flash", map_cs); -#endif #ifdef CONFIG_MTD_CFI + mtd_cs = do_map_probe("cfi_probe", map_cs); +#endif +#ifdef CONFIG_MTD_JEDECPROBE if (!mtd_cs) { - mtd_cs = do_map_probe("cfi_probe", map_cs); + mtd_cs = do_map_probe("jedec_probe", map_cs); } #endif return mtd_cs; } -/* +/* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct * so that MTD partitions can cross chip boundries. @@ -217,22 +265,17 @@ { struct mtd_info *mtd_cse0; struct mtd_info *mtd_cse1; - struct mtd_info *mtd_nand = NULL; struct mtd_info *mtd_total; - struct mtd_info *mtds[3]; + struct mtd_info *mtds[2]; int count = 0; if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL) mtds[count++] = mtd_cse0; if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL) mtds[count++] = mtd_cse1; + -#ifdef CONFIG_ETRAX_NANDFLASH - if ((mtd_nand = crisv32_nand_flash_probe()) != NULL) - mtds[count++] = mtd_nand; -#endif - - if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) { + if (!mtd_cse0 && !mtd_cse1) { /* No chip found. */ return NULL; } @@ -248,7 +291,7 @@ */ mtd_total = mtd_concat_create(mtds, count, - "cse0+cse1+nand"); + "cse0+cse1"); #else printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " "(mis)configuration!\n", map_cse0.name, map_cse1.name); @@ -260,57 +303,155 @@ /* The best we can do now is to only use what we found * at cse0. - */ + */ mtd_total = mtd_cse0; map_destroy(mtd_cse1); } } else { - mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand; - + mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1; + } return mtd_total; } -extern unsigned long crisv32_nand_boot; -extern unsigned long crisv32_nand_cramfs_offset; - /* * Probe the flash chip(s) and, if it succeeds, read the partition-table * and register the partitions with MTD. */ static int __init init_axis_flash(void) { - struct mtd_info *mymtd; + struct mtd_info *main_mtd; + struct mtd_info *aux_mtd = NULL; int err = 0; int pidx = 0; struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; - int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n"; - static char page[512]; + int ptable_ok = 0; + static char page[PAGESIZE]; size_t len; + int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */ + int part; + + /* We need a root fs. If it resides in RAM, we need to use an + * MTDRAM device, so it must be enabled in the kernel config, + * but its size must be configured as 0 so as not to conflict + * with our usage. + */ +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + if (!romfs_in_flash && !nand_boot) { + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device; configure CONFIG_MTD_MTDRAM with size = 0!\n"); + panic("This kernel cannot boot from RAM!\n"); + } +#endif #ifndef CONFIG_ETRAXFS_SIM - mymtd = flash_probe(); - mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page); - ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET); + main_mtd = flash_probe(); + if (main_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NOR flash memory.\n", + main_mtd->name, main_mtd->size); + +#ifdef CONFIG_ETRAX_NANDFLASH + aux_mtd = crisv32_nand_flash_probe(); + if (aux_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n", + aux_mtd->name, aux_mtd->size); - if (!mymtd) { +#ifdef CONFIG_ETRAX_NANDBOOT + { + struct mtd_info *tmp_mtd; + + printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, " + "making NAND flash primary device.\n"); + tmp_mtd = main_mtd; + main_mtd = aux_mtd; + aux_mtd = tmp_mtd; + } +#endif /* CONFIG_ETRAX_NANDBOOT */ +#endif /* CONFIG_ETRAX_NANDFLASH */ + + if (!main_mtd && !aux_mtd) { /* There's no reason to use this module if no flash chip can * be identified. Make sure that's understood. */ printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); - } else { - printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", - mymtd->name, mymtd->size); - axisflash_mtd = mymtd; } - if (mymtd) { - mymtd->owner = THIS_MODULE; +#if 0 /* Dump flash memory so we can see what is going on */ + if (main_mtd) { + int sectoraddr, i; + for (sectoraddr = 0; sectoraddr < 2*65536+4096; sectoraddr += PAGESIZE) { + main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len, page); + printk(KERN_INFO + "Sector at %d (length %d):\n", + sectoraddr, len); + for (i = 0; i < PAGESIZE; i += 16) { + printk(KERN_INFO + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + page[i] & 255, page[i+1] & 255, + page[i+2] & 255, page[i+3] & 255, + page[i+4] & 255, page[i+5] & 255, + page[i+6] & 255, page[i+7] & 255, + page[i+8] & 255, page[i+9] & 255, + page[i+10] & 255, page[i+11] & 255, + page[i+12] & 255, page[i+13] & 255, + page[i+14] & 255, page[i+15] & 255); + } + + } + } +#endif + + if (main_mtd) { + main_mtd->owner = THIS_MODULE; + axisflash_mtd = main_mtd; + + loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR; + + pidx++; /* First partition (rescue) is always set to the default. */ +#ifdef CONFIG_ETRAX_NANDBOOT + /* We know where the partition table should be located, + * it will be in first good block after that. + */ + int blockstat; + do { + blockstat = main_mtd->block_isbad(main_mtd, ptable_sector); + if (blockstat < 0) + ptable_sector = 0; /* read error */ + else if (blockstat) + ptable_sector += main_mtd->erasesize; + } while (blockstat && ptable_sector); +#endif + if (ptable_sector) { + main_mtd->read(main_mtd, ptable_sector, PAGESIZE, &len, page); + ptable_head = &((struct partitiontable *) page)->head; + } + +#if 0 /* Dump partition table so we can see what is going on */ + printk(KERN_INFO + "axisflashmap: flash read %d bytes at 0x%08x, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + len, CONFIG_ETRAX_PTABLE_SECTOR, + page[0] & 255, page[1] & 255, + page[2] & 255, page[3] & 255, + page[4] & 255, page[5] & 255, + page[6] & 255, page[7] & 255); + printk(KERN_INFO + "axisflashmap: partition table offset %d, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + PARTITION_TABLE_OFFSET, + page[PARTITION_TABLE_OFFSET+0] & 255, + page[PARTITION_TABLE_OFFSET+1] & 255, + page[PARTITION_TABLE_OFFSET+2] & 255, + page[PARTITION_TABLE_OFFSET+3] & 255, + page[PARTITION_TABLE_OFFSET+4] & 255, + page[PARTITION_TABLE_OFFSET+5] & 255, + page[PARTITION_TABLE_OFFSET+6] & 255, + page[PARTITION_TABLE_OFFSET+7] & 255); +#endif } - pidx++; /* First partition is always set to the default. */ if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) && (ptable_head->size < @@ -323,7 +464,6 @@ /* Looks like a start, sane length and end of a * partition table, lets check csum etc. */ - int ptable_ok = 0; struct partitiontable_entry *max_addr = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head) + @@ -331,7 +471,7 @@ unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; unsigned char *p; unsigned long csum = 0; - + ptable = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head)); @@ -343,108 +483,177 @@ csum += *p++; csum += *p++; csum += *p++; - } + } ptable_ok = (csum == ptable_head->checksum); /* Read the entries and use/show the info. */ - printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + printk(KERN_INFO "axisflashmap: " + "Found a%s partition table at 0x%p-0x%p.\n", (ptable_ok ? " valid" : "n invalid"), ptable_head, max_addr); /* We have found a working bootblock. Now read the - * partition table. Scan the table. It ends when - * there is 0xffffffff, that is, empty flash. + * partition table. Scan the table. It ends with 0xffffffff. */ while (ptable_ok - && ptable->offset != 0xffffffff + && ptable->offset != PARTITIONTABLE_END_MARKER && ptable < max_addr - && pidx < MAX_PARTITIONS) { + && pidx < MAX_PARTITIONS - 1) { - axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0); - axis_partitions[pidx].size = ptable->size; - - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + axis_partitions[pidx].offset = offset + ptable->offset; +#ifdef CONFIG_ETRAX_NANDFLASH + if (main_mtd->type == MTD_NANDFLASH) { + axis_partitions[pidx].size = + (((ptable+1)->offset == + PARTITIONTABLE_END_MARKER) ? + main_mtd->size : + ((ptable+1)->offset + offset)) - + (ptable->offset + offset); + + } else +#endif /* CONFIG_ETRAX_NANDFLASH */ + axis_partitions[pidx].size = ptable->size; +#ifdef CONFIG_ETRAX_NANDBOOT + /* Save partition number of jffs2 ro partition. + * Needed if RAM booting or root file system in RAM. + */ + if (!nand_boot && + ram_rootfs_partition < 0 && /* not already set */ + ptable->type == PARTITION_TYPE_JFFS2 && + (ptable->flags & PARTITION_FLAGS_READONLY_MASK) == + PARTITION_FLAGS_READONLY) + ram_rootfs_partition = pidx; +#endif /* CONFIG_ETRAX_NANDBOOT */ pidx++; ptable++; } - use_default_ptable = !ptable_ok; } - if (romfs_in_flash) { - /* Add an overlapping device for the root partition (romfs). */ + /* Decide whether to use default partition table. */ + /* Only use default table if we actually have a device (main_mtd) */ - axis_partitions[pidx].name = "romfs"; - if (crisv32_nand_boot) { - char* data = kmalloc(1024, GFP_KERNEL); - int len; - int offset = crisv32_nand_cramfs_offset & ~(1024-1); - char* tmp; - - mymtd->read(mymtd, offset, 1024, &len, data); - tmp = &data[crisv32_nand_cramfs_offset % 512]; - axis_partitions[pidx].size = *(unsigned*)(tmp + 4); - axis_partitions[pidx].offset = crisv32_nand_cramfs_offset; - kfree(data); - } else { - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; - } + struct mtd_partition *partition = &axis_partitions[0]; + if (main_mtd && !ptable_ok) { + memcpy(axis_partitions, axis_default_partitions, + sizeof(axis_default_partitions)); + pidx = NUM_DEFAULT_PARTITIONS; + ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO; + } + /* Add artificial partitions for rootfs if necessary */ + if (romfs_in_flash) { + /* rootfs is in directly accessible flash memory = NOR flash. + Add an overlapping device for the rootfs partition. */ + printk(KERN_INFO "axisflashmap: Adding partition for " + "overlapping root file system image\n"); + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].name = "romfs"; 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); + ram_rootfs_partition = -1; pidx++; } - - if (mymtd) { - if (use_default_ptable) { - printk(KERN_INFO " Using default partition table.\n"); - err = add_mtd_partitions(mymtd, axis_default_partitions, - NUM_DEFAULT_PARTITIONS); - } else { - err = add_mtd_partitions(mymtd, axis_partitions, pidx); + else if (romfs_length && !nand_boot) { + /* romfs exists in memory, but not in flash, so must be in RAM. + * Configure an MTDRAM partition. */ + if (ram_rootfs_partition < 0) { /* none set yet */ + ram_rootfs_partition = pidx; /* put it at the end */ + pidx++; } + printk(KERN_INFO "axisflashmap: Adding partition for " + "root file system image in RAM\n"); + axis_partitions[ram_rootfs_partition].size = romfs_length; + axis_partitions[ram_rootfs_partition].offset = romfs_start; + axis_partitions[ram_rootfs_partition].name = "romfs"; + axis_partitions[ram_rootfs_partition].mask_flags |= + MTD_WRITEABLE; + } - if (err) { - panic("axisflashmap could not add MTD partitions!\n"); - } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (main_mtd) { + main_partition.size = main_mtd->size; + err = add_mtd_partitions(main_mtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); } -/* CONFIG_EXTRAXFS_SIM */ #endif - if (!romfs_in_flash) { - /* Create an RAM device for the root partition (romfs). */ + /* Now, register all partitions with mtd. + * We do this one at a time so we can slip in an MTDRAM device + * in the proper place if required. */ + + for (part = 0; part < pidx; part++) { + if (part == ram_rootfs_partition) { + /* add MTDRAM partition here */ + 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 "axisflashmap: Adding RAM partition " + "for rootfs image.\n"); + err = mtdram_init_device(mtd_ram, + (void *)partition[part].offset, + partition[part].size, + partition[part].name); + if (err) + panic("axisflashmap: Could not initialize " + "MTD RAM device!\n"); + /* JFFS2 likes to have an erasesize. Keep potential + * JFFS2 rootfs happy by providing one. Since image + * was most likely created for main mtd, use that + * erasesize, if available. Otherwise, make a guess. */ + mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize : + CONFIG_ETRAX_PTABLE_SECTOR); + } else { + err = add_mtd_partitions(main_mtd, + &partition[part], 1); + if (err) + panic("axisflashmap: Could not add mtd " + "partition %d\n", part); + } + } -#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; +#endif /* CONFIG_EXTRAXFS_SIM */ - mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { - panic("axisflashmap couldn't allocate memory for " - "mtd_info!\n"); - } +#ifdef CONFIG_ETRAXFS_SIM + /* For simulator, always use a RAM partition. + * The rootfs will be found after the kernel in RAM, + * with romfs_start and romfs_end indicating location and size. + */ + struct mtd_info *mtd_ram; + + mtd_ram = (struct mtd_info *)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, romfs_start, romfs_length); + printk(KERN_INFO "axisflashmap: Adding RAM partition for romfs, " + "at %u, size %u\n", + (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 /* CONFIG_EXTRAXFS_SIM */ + +#ifndef CONFIG_ETRAXFS_SIM + if (aux_mtd) { + aux_partition.size = aux_mtd->size; + err = add_mtd_partitions(aux_mtd, &aux_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "aux mtd device!\n"); - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { - panic("axisflashmap could not initialize MTD RAM " - "device!\n"); - } -#endif } +#endif /* CONFIG_EXTRAXFS_SIM */ return err; } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/board_mmcspi.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/board_mmcspi.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/board_mmcspi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/board_mmcspi.c 2007-01-29 15:51:19.000000000 +0100 @@ -0,0 +1,686 @@ +/* + * Somewhat generic "board-side" code to support SPI drivers for chips + * with a CRIS v32 and later. Not really board-specific, and only for + * registration of SPI devices for MMC, hence the "mmcspi" part of the + * name instead of a proper board name. + * + * Copyright (c) 2007 Axis Communications AB + * + * TODO: SDIO interrupt-pin support (though can't be done until + * there's support added in both the mmc_spi and the mmc frameworks). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* We need some housekeeping. */ +#define CONCAT_(a, b) a ## b +#define CONCAT(a, b) CONCAT_(a, b) +#define CONCAT3(a, b, c) CONCAT(CONCAT(a, b), c) + +/* Grr. Why not define them as usual in autoconf, #ifdef REVEAL_MODULES? */ +#if !defined(CONFIG_SPI_ETRAX_SSER) && defined(CONFIG_SPI_ETRAX_SSER_MODULE) +#define CONFIG_SPI_ETRAX_SSER +#endif + +#if !defined(CONFIG_ETRAX_SPI_SSER0) && defined(CONFIG_ETRAX_SPI_SSER0_MODULE) +#define CONFIG_ETRAX_SPI_SSER0 +#endif + +#if !defined(CONFIG_ETRAX_SPI_SSER1) && defined(CONFIG_ETRAX_SPI_SSER1_MODULE) +#define CONFIG_ETRAX_SPI_SSER1 +#endif + +#if !defined(CONFIG_SPI_ETRAX_GPIO) && defined(CONFIG_SPI_ETRAX_GPIO_MODULE) +#define CONFIG_SPI_ETRAX_GPIO +#endif + +#define CONFIGURED_PIN(x) ((x) != NULL && *(x) != 0 && *(x) != ' ') + +/* Helper function to configure an iopin for input. */ + +static int crisv32_config_pin_in(struct crisv32_iopin *pin, const char *name) +{ + int ret = crisv32_io_get_name(pin, name); + if (ret) + return ret; + + crisv32_io_set_dir(pin, crisv32_io_dir_in); + return 0; +} + +/* + * Writable data pointed to by the constant parts of each MMC_SPI bus + * interface. Should only hold MMC-specific state (for the + * MMC-specific pins), nothing SPI-generic. + */ + +struct crisv32_mmc_spi_pinstate { + struct crisv32_iopin write_protect_pin; + struct crisv32_iopin card_detect_pin; + + /* We must poll for card-detect, which we do each: */ +#define CARD_DETECT_CHECK_INTERVAL (2*HZ) + /* We poll using a self-arming workqueue function. */ + struct work_struct cd_work; + + /* When we detect a change in card presence, we call this + * function, supplied by the mmc_spi framework: */ + irqreturn_t (*detectfunc)(int, void *); + + /* + * We call that function with this parameter as the second + * one. We must assume the other parameters are unused, as we + * don't have an interrupt context. + */ + void *detectfunc_param2; + + /* State for the card-detect worker. */ + int card_present; +}; + +/* Rearming "work" function for card-detect polling. */ + +static void crisv32_cd_worker(void *x) +{ + struct crisv32_mmc_spi_pinstate *state = x; + + /* It's a pull-up, so a card is present when 0. */ + int card_present = crisv32_io_rd(&state->card_detect_pin) == 0; + + if (card_present != state->card_present) { + state->card_present = card_present; + state->detectfunc(0, state->detectfunc_param2); + } + schedule_delayed_work(&state->cd_work, CARD_DETECT_CHECK_INTERVAL); +} + +/* The parts of MMC-specific configuration common to GPIO and SSER. */ + +static int crisv32_mmcspi_config_common(struct spi_device *spidev, + irqreturn_t (*intfunc)(int, void *), + struct mmc_host *mmc_host, + const struct crisv32_mmc_spi_pindata *pd) +{ + int ret = 0; + struct crisv32_mmc_spi_pinstate *ps = pd->pinstate; + + /* + * If we don't have a card-detect pin, the card must be + * present at startup (including insmod/modprobe). No + * re-scans are done if no card is present at that time. + */ + if (CONFIGURED_PIN(pd->card_detect)) { + ret = crisv32_config_pin_in(&ps->card_detect_pin, + pd->card_detect); + + if (ret != 0) + goto bad_card_detect; + + /* Need to cast away const from pd, unfortunately. */ + INIT_WORK(&ps->cd_work, crisv32_cd_worker, (void *) ps); + ps->card_present = 1; + ps->detectfunc = intfunc; + ps->detectfunc_param2 = mmc_host; + crisv32_cd_worker(ps); + } else + ps->detectfunc = NULL; + + /* + * We set up for checking for presence of a write-protect pin + * as pin.port being non-NULL. + */ + memset(&ps->write_protect_pin, 0, sizeof ps->write_protect_pin); + if (CONFIGURED_PIN(pd->write_protect)) { + ret = crisv32_config_pin_in(&ps->write_protect_pin, + pd->write_protect); + + if (ret != 0) + goto bad_write_protect; + } + + return 0; + + bad_write_protect: + if (ps->detectfunc) { + cancel_rearming_delayed_work(&ps->cd_work); + ps->detectfunc = NULL; + } + + bad_card_detect: + return ret; +} + +/* A function to undo crisv32_mmcspi_config_common. */ + +static void crisv32_mmcspi_deconfig_common(const struct + crisv32_mmc_spi_pindata *pd) +{ + if (pd->pinstate->detectfunc) { + cancel_rearming_delayed_work(&pd->pinstate->cd_work); + pd->pinstate->detectfunc = NULL; + } + + /* We don't have to undo the pin allocations, being GPIO. */ +} + +/* Helper function for write-protect sense. */ + +static int crisv32_mmcspi_get_ro_helper(struct crisv32_mmc_spi_pinstate *ps) +{ + return ps->write_protect_pin.port != NULL + && crisv32_io_rd(&ps->write_protect_pin) != 0; +} + +/* The hardware-interface-specific SPI+MMC parts. */ + +#ifdef CONFIG_SPI_ETRAX_SSER +static const char crisv32_matching_spi_sser_driver_name[] __init_or_module + = "spi_crisv32_sser"; + +/* + * Make up something we can use in tables and function without + * #ifdef-cluttering. + */ +#ifdef CONFIG_ETRAX_SPI_SSER0_DMA +#define WITH_ETRAX_SPI_SSER0_DMA 1 +#else +#define WITH_ETRAX_SPI_SSER0_DMA 0 +#endif +#ifdef CONFIG_ETRAX_SPI_SSER1_DMA +#define WITH_ETRAX_SPI_SSER1_DMA 1 +#else +#define WITH_ETRAX_SPI_SSER1_DMA 0 +#endif + +/* Write-protect sense for the SSER interface. */ + +static int crisv32_mmcspi_sser_get_ro(struct device *dev) +{ + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_sser_hwdata *sser_defs + = spidev->controller_data; + struct crisv32_mmc_spi_pindata *pd = &sser_defs->mmc; + return crisv32_mmcspi_get_ro_helper(pd->pinstate); +} + +/* Initialize the MMC-specific parts of the MMC+SPI interface, SSER. */ + +static int crisv32_mmcspi_sser_init(struct device *dev, + irqreturn_t (*intfunc)(int, void *), + void *xmmc_host) +{ + struct mmc_host *mmc_host = xmmc_host; + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_sser_hwdata *sser_defs + = spidev->controller_data; + int ret = crisv32_mmcspi_config_common(spidev, intfunc, mmc_host, + &sser_defs->mmc); + if (ret != 0) + return ret; + + mmc_host->f_max = spidev->max_speed_hz; + + /* + * Let's just set this to ceil(100e6/65536). It wouldn't be + * hard to change the base frequency to support down to 450Hz, + * but there's no apparent requirement from known hardware. + * Would also require adjustments to the SPI code, not just + * here. + * + * On second thought, let's not set f_min unconditionally, as + * mmc doesn't treat it as a limit, but as the frequency to + * use at initialization. We stick with what mmc_spi sets, if + * it fits. + */ + if (mmc_host->f_min < 1526) + mmc_host->f_min = 1526; + + dev_info(dev, "CRIS v32 mmc_spi support hooked to SPI on sser%d" + " (cd: %s, wp: %s)\n", + spidev->master->bus_num, + CONFIGURED_PIN(sser_defs->mmc.card_detect) + ? sser_defs->mmc.card_detect : "(none)", + CONFIGURED_PIN(sser_defs->mmc.write_protect) + ? sser_defs->mmc.write_protect : "(none)"); + return 0; +} + +/* Similarly, to undo crisv32_mmcspi_sser_init. */ + +static void crisv32_mmcspi_sser_exit(struct device *dev, void *xmmc_host) +{ + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_sser_hwdata *sser_defs + = spidev->controller_data; + + crisv32_mmcspi_deconfig_common(&sser_defs->mmc); +} + +/* + * Connect sser and DMA channels and return the proper numbers to use. + * + * This function exists really only to avoid having the following constants + * tabled: regi_sserN, SSERn_INTR_VECT, pinmux_sserN, SYNC_SERn_TX_DMA_NBR, + * SYNC_SERn_RX_DMA_NBR dma_sserN, regi_dmaM, regi_dmaN, DMAm_INTR_VECT, + * DMAn_INTR_VECT. I might have missed some. :-) + */ +static int crisv32_allocate_sser(struct crisv32_regi_n_int *sserp, + struct crisv32_regi_n_int *dmainp, + struct crisv32_regi_n_int *dmaoutp, + u32 sserno) +{ + int ret; + u32 regi_sser = sserno == 0 ? regi_sser0 : regi_sser1; + u32 sser_irq = sserno == 0 ? SSER0_INTR_VECT : SSER1_INTR_VECT; + BUG_ON(sserno > 1 || sserp == NULL); + + ret = crisv32_pinmux_alloc_fixed(sserno == 0 + ? pinmux_sser0 : pinmux_sser1); + if (ret != 0) + return ret; + + sserp->regi = regi_sser; + sserp->irq = sser_irq; + + if (dmaoutp) { + crisv32_request_dma(sserno == 0 + ? SYNC_SER0_TX_DMA_NBR + : SYNC_SER1_TX_DMA_NBR, + "SD/MMC SPI dma tr", + DMA_VERBOSE_ON_ERROR | DMA_PANIC_ON_ERROR, + /* Let's be brave and ask for the + max. Except it's simplex, so + let's request half the max + speed in either direction. */ + 50 * 1000 * 1000 / 8 / 2, + sserno == 0 ? dma_sser0 : dma_sser1); + dmaoutp->regi = sserno == 0 + ? CONCAT(regi_dma, SYNC_SER0_TX_DMA_NBR) + : CONCAT(regi_dma, SYNC_SER1_TX_DMA_NBR); + dmaoutp->irq = sserno == 0 + ? CONCAT3(DMA, SYNC_SER0_TX_DMA_NBR, _INTR_VECT) + : CONCAT3(DMA, SYNC_SER1_TX_DMA_NBR, _INTR_VECT); + } + + if (dmainp) { + crisv32_request_dma(sserno == 0 + ? SYNC_SER0_RX_DMA_NBR + : SYNC_SER1_RX_DMA_NBR, + "SD/MMC SPI dma rec", + DMA_VERBOSE_ON_ERROR | DMA_PANIC_ON_ERROR, + 50 * 1000 * 1000 / 8 / 2, + sserno == 0 ? dma_sser0 : dma_sser1); + dmainp->regi = sserno == 0 + ? CONCAT(regi_dma, SYNC_SER0_RX_DMA_NBR) + : CONCAT(regi_dma, SYNC_SER1_RX_DMA_NBR); + dmainp->irq = sserno == 0 + ? CONCAT3(DMA, SYNC_SER0_RX_DMA_NBR, _INTR_VECT) + : CONCAT3(DMA, SYNC_SER1_RX_DMA_NBR, _INTR_VECT); + } + + return 0; +} + +/* Undo whatever allocations et. al. that crisv32_allocate_sser did. */ + +static void crisv32_free_sser(u32 sserno, int with_dma) +{ + int ret; + + if (with_dma) { + crisv32_free_dma(sserno == 0 + ? SYNC_SER0_RX_DMA_NBR + : SYNC_SER1_RX_DMA_NBR); + crisv32_free_dma(sserno == 0 + ? SYNC_SER0_TX_DMA_NBR + : SYNC_SER1_TX_DMA_NBR); + } + + ret = crisv32_pinmux_dealloc_fixed(sserno == 0 + ? pinmux_sser0 : pinmux_sser1); + + if (ret != 0) + panic("%s: crisv32_pinmux_dealloc_fixed returned %d\n", + __FUNCTION__, ret); +} + +/* Convenience-macro to avoid typos causing diffs between sser ports. */ + +#define SSER_CDATA(n) \ + { \ + { \ + .using_dma = WITH_ETRAX_SPI_SSER##n##_DMA, \ + .iface_allocate = crisv32_allocate_sser##n, \ + .iface_free = crisv32_free_sser##n \ + }, \ + { \ + .card_detect \ + = CONFIG_ETRAX_SPI_MMC_CD_SSER##n##_PIN,\ + .write_protect \ + = CONFIG_ETRAX_SPI_MMC_WP_SSER##n##_PIN,\ + .pinstate \ + = &crisv32_mmcspi_sser##n##_pinstate \ + } \ + } + +#ifdef CONFIG_ETRAX_SPI_SSER0 + +/* Allocate hardware to go with sser0 and fill in the right numbers. */ + +static int crisv32_allocate_sser0(struct crisv32_regi_n_int *sserp, + struct crisv32_regi_n_int *dmainp, + struct crisv32_regi_n_int *dmaoutp) +{ + return crisv32_allocate_sser(sserp, dmainp, dmaoutp, 0); +} + +/* Undo those allocations. */ + +static void crisv32_free_sser0(void) +{ + crisv32_free_sser(0, WITH_ETRAX_SPI_SSER0_DMA); +} + +static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_sser0_pinstate; +static const struct crisv32_mmc_spi_sser_hwdata crisv32_mmcspi_sser0_cdata + = SSER_CDATA(0); +#endif /* CONFIG_ETRAX_SPI_SSER0 */ + +#ifdef CONFIG_ETRAX_SPI_SSER1 + +/* Allocate hardware to go with sser0 and fill in the right numbers. */ + +static int crisv32_allocate_sser1(struct crisv32_regi_n_int *sserp, + struct crisv32_regi_n_int *dmainp, + struct crisv32_regi_n_int *dmaoutp) +{ + return crisv32_allocate_sser(sserp, dmainp, dmaoutp, 1); +} + +/* Undo those allocations. */ + +static void crisv32_free_sser1(void) +{ + crisv32_free_sser(1, WITH_ETRAX_SPI_SSER1_DMA); +} + +static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_sser1_pinstate; +static const struct crisv32_mmc_spi_sser_hwdata crisv32_mmcspi_sser1_cdata + = SSER_CDATA(1); + +#endif /* CONFIG_ETRAX_SPI_SSER1 */ + +static struct mmc_spi_platform_data crisv32_mmcspi_sser_pdata = { + .init = crisv32_mmcspi_sser_init, + .exit = __devexit_p(crisv32_mmcspi_sser_exit), + .detect_delay = 0, + .get_ro = crisv32_mmcspi_sser_get_ro, + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .setpower = NULL +}; + +#endif /* CONFIG_SPI_ETRAX_SSER */ + +#ifdef CONFIG_SPI_ETRAX_GPIO + +static const char crisv32_matching_spi_gpio_driver_name[] __init_or_module + = "spi_crisv32_gpio"; + +/* Write-protect sense for the GPIO interface. */ + +static int crisv32_mmcspi_gpio_get_ro(struct device *dev) +{ + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_gpio_hwdata *gpio_defs + = spidev->controller_data; + struct crisv32_mmc_spi_pindata *pd = &gpio_defs->mmc; + + return crisv32_mmcspi_get_ro_helper(pd->pinstate); +} + +/* Initialize the MMC-specific parts of the MMC+SPI interface, GPIO. */ + +static int crisv32_mmcspi_gpio_init(struct device *dev, + irqreturn_t (*intfunc)(int, void *), + void *xmmc_host) +{ + struct mmc_host *mmc_host = xmmc_host; + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_gpio_hwdata *gpio_defs + = spidev->controller_data; + int ret = crisv32_mmcspi_config_common(spidev, intfunc, mmc_host, + &gpio_defs->mmc); + if (ret) + return ret; + + mmc_host->f_max = spidev->max_speed_hz; + + /* + * We don't set f_min, as the mmc code doesn't treat it as a + * limit, but as the frequency to use at initialization. We + * stick with what mmc_spi sets. + */ + dev_info(dev, "CRIS v32 mmc_spi support hooked to SPI on GPIO" + " (bus #%d, cd: %s, wp: %s)\n", + spidev->master->bus_num, + CONFIGURED_PIN(gpio_defs->mmc.card_detect) + ? gpio_defs->mmc.card_detect : "(none)", + CONFIGURED_PIN(gpio_defs->mmc.write_protect) + ? gpio_defs->mmc.write_protect : "(none)"); + return 0; +} + +/* Similarly, to undo crisv32_mmcspi_gpio_init. */ + +static void crisv32_mmcspi_gpio_exit(struct device *dev, void *xmmc_host) +{ + struct spi_device *spidev = to_spi_device(dev); + struct crisv32_mmc_spi_gpio_hwdata *gpio_defs + = spidev->controller_data; + + crisv32_mmcspi_deconfig_common(&gpio_defs->mmc); +} + +static const struct mmc_spi_platform_data crisv32_mmcspi_gpio_pdata = { + .init = crisv32_mmcspi_gpio_init, + .exit = __devexit_p(crisv32_mmcspi_gpio_exit), + .detect_delay = 0, + .get_ro = crisv32_mmcspi_gpio_get_ro, + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .setpower = NULL +}; + +static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_gpio_pinstate; +static const struct crisv32_mmc_spi_gpio_hwdata crisv32_mmcspi_gpio_cdata = { + { + .cs = CONFIG_ETRAX_SPI_CS_PIN, + .miso = CONFIG_ETRAX_SPI_DATAIN_PIN, + .mosi = CONFIG_ETRAX_SPI_DATAOUT_PIN, + .sclk = CONFIG_ETRAX_SPI_CLK_PIN + }, + { + .card_detect = CONFIG_ETRAX_SPI_MMC_CD_GPIO_PIN, + .write_protect = CONFIG_ETRAX_SPI_MMC_WP_GPIO_PIN, + .pinstate = &crisv32_mmcspi_gpio_pinstate + } +}; +#endif /* CONFIG_SPI_ETRAX_GPIO */ + +struct crisv32_s_b_i_and_driver_info { + struct spi_board_info sbi; + const char *driver_name; + + /* + * We have to adjust the platform device at time of creation + * below, to allow the Linux DMA framework to handle DMA. In + * the name of simplicity, we state the dma:able property + * twice, a bit redundantly; here and in the controller_data + * structure pointed to by spi_board_info. + */ + int dma_able; +}; + +/* Convenience-macro to avoid typos causing diffs between sser ports. */ + +#define SSER_CONFIG(n) \ + { \ + /* \ + * No meaningful values for .irq or .chip_select, \ + * so we leave them out. \ + */ \ + { \ + .modalias = "mmc_spi", \ + .platform_data \ + = &crisv32_mmcspi_sser_pdata, \ + /* \ + * Casting to avoid a compiler const-warning. \ + */ \ + .controller_data \ + = ((void *) \ + &crisv32_mmcspi_sser##n##_cdata), \ + .max_speed_hz = 50 * 1000 * 1000, \ + .bus_num = n, \ + /* \ + * We only provide one mode, so it must be \ + * default. \ + */ \ + .mode = SPI_MODE_3 \ + }, \ + crisv32_matching_spi_sser_driver_name, \ + WITH_ETRAX_SPI_SSER##n##_DMA \ + } + +static const struct crisv32_s_b_i_and_driver_info +crisv32_mmcspi_devices[] __initdata = { +#ifdef CONFIG_ETRAX_SPI_SSER0 + SSER_CONFIG(0), +#endif +#ifdef CONFIG_ETRAX_SPI_SSER1 + SSER_CONFIG(1), +#endif +#ifdef CONFIG_SPI_ETRAX_GPIO + { + { + .modalias = "mmc_spi", + .platform_data = &crisv32_mmcspi_gpio_pdata, + /* Casting to avoid a compiler const-warning. */ + .controller_data + = (void *) &crisv32_mmcspi_gpio_cdata, + + /* No, we can't even reach this number with GPIO. */ + .max_speed_hz = 5 * 1000 * 1000, + .bus_num = 2 + }, + crisv32_matching_spi_gpio_driver_name, + 0 + }, +#endif +}; + +/* Must not be __initdata. */ +static const char controller_data_ptr_name[] = "controller_data_ptr"; +static u64 allmem_mask = ~(u32) 0; + +/* + * We have to register the SSER and GPIO "platform_device"s separately + * from the "spi_board_info"s in order to have the drivers separated + * from the hardware instance info. + */ + +static int __init crisv32_register_mmcspi(void) +{ + int ret; + void *retp; + const struct crisv32_s_b_i_and_driver_info *sbip; + + for (sbip = crisv32_mmcspi_devices; + sbip < crisv32_mmcspi_devices + + ARRAY_SIZE(crisv32_mmcspi_devices); + sbip++) { + + /* + * We have to pass the controller data as a device + * "resource" (FIXME: too?), so it can be available + * for the setup *before* starting the SPI core - + * which is where .controller_data makes it to the SPI + * structure! + */ + struct resource mmcspi_res = { + .start = (resource_size_t) sbip->sbi.controller_data, + .end = (resource_size_t) sbip->sbi.controller_data, + .name = controller_data_ptr_name + }; + + /* + * Presumably we could pass the irqs and register + * addresses and... as individual resources here + * instead of privately in spi_board_info + * .controller_data, and make that part a bit more + * readable. Maybe not worthwhile. + */ + /* Need to cast away const here. FIXME: fix the pdrs API. */ + retp = platform_device_alloc(sbip->driver_name, + sbip->sbi.bus_num); + if (IS_ERR_VALUE(PTR_ERR(retp))) + return PTR_ERR(retp); + + /* + * We can't use platform_device_register_simple as we + * want to set stuff in the device to mark that we'd + * prefer buffers explicitly passed as DMA-able. + * Disabling/omitting the "if" below, cause testing of + * the non-DMA-mem execution path of a DMA-capable + * driver. + */ + if (sbip->dma_able) { + struct platform_device *pdev = retp; + + pdev->dev.dma_mask = &allmem_mask; + pdev->dev.coherent_dma_mask = allmem_mask; + } + + if ((ret = platform_device_add_resources(retp, &mmcspi_res, 1)) != 0 + || (ret = platform_device_add(retp)) != 0) { + platform_device_put(retp); + return ret; + } + + /* + * The cost of keeping all info rooted at + * crisv32_mmcspi_devices is the allocation overhead + * for allocating separately each board info (in the + * unlikely event that there's more than one). + */ + ret = spi_register_board_info(&sbip->sbi, 1); + if (ret != 0) + return ret; + } + + return 0; +} +#ifdef MODULE +#error "Non-module because spi_register_board_info is one-way; there's no unregister function" +#endif + +/* + * Needs to be called before __initcall/module_init because the SPI + * bus scanning happens when the actual driver registers with the SPI + * machinery (spi_bitbang_start/spi_register_master), not when the + * device registers (spi_register_board_info). + */ +subsys_initcall(crisv32_register_mmcspi); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/cryptocop.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/cryptocop.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/cryptocop.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/cryptocop.c 2007-01-09 10:29:20.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $ +/* $Id: cryptocop.c,v 1.22 2007/01/09 09:29:20 starvik Exp $ * * Stream co-processor driver for the ETRAX FS * @@ -1718,7 +1718,7 @@ * i = i + 1 * } * i = Nk - * + * * while (i < (Nb * (Nr + 1))) { * temp = w[i - 1] * if ((i mod Nk) == 0) { @@ -1886,7 +1886,7 @@ } static irqreturn_t -dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) +dma_done_interrupt(int irq, void *dev_id) { struct cryptocop_prio_job *done_job; reg_dma_rw_ack_intr ack_intr = { @@ -2226,6 +2226,7 @@ &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out))); /* Start input DMA. */ + flush_dma_context(&pj->iop->ctx_in); DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in)); /* Start output DMA. */ @@ -3459,7 +3460,7 @@ int err; int i; static int initialized = 0; - + if (initialized) return 0; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/gpio.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/gpio.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/gpio.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/gpio.c 2007-01-09 10:29:20.000000000 +0100 @@ -1,68 +1,15 @@ -/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ - * +/* * ETRAX CRISv32 general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 1999-2006 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G, * port to ETRAX FS. * - * $Log: gpio.c,v $ - * Revision 1.16 2005/06/19 17:06:49 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.15 2005/05/25 08:22:20 starvik - * Changed GPIO port order to fit packages/devices/axis-2.4. - * - * Revision 1.14 2005/04/24 18:35:08 starvik - * Updated with final register headers. - * - * Revision 1.13 2005/03/15 15:43:00 starvik - * dev_id needs to be supplied for shared IRQs. - * - * Revision 1.12 2005/03/10 17:12:00 starvik - * Protect alarm list with spinlock. - * - * Revision 1.11 2005/01/05 06:08:59 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.10 2004/11/19 08:38:31 starvik - * Removed old crap. - * - * Revision 1.9 2004/05/14 07:58:02 starvik - * Merge of changes from 2.4 - * - * Revision 1.8 2003/09/11 07:29:50 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.7 2003/07/10 13:25:46 starvik - * Compiles for 2.5.74 - * Lindented ethernet.c - * - * Revision 1.6 2003/07/04 08:27:46 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/10 08:26:37 johana - * Etrax -> ETRAX CRISv32 - * - * Revision 1.4 2003/06/05 14:22:48 johana - * Initialise some_alarms. - * - * Revision 1.3 2003/06/05 10:15:46 johana - * New INTR_VECT macros. - * Enable interrupts in global config. - * - * Revision 1.2 2003/06/03 15:52:50 johana - * Initial CRIS v32 version. - * - * Revision 1.1 2003/06/03 08:53:15 johana - * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. - * */ - #include #include #include @@ -85,6 +32,13 @@ #include #include +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + + /* The following gio ports on ETRAX FS is available: * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge * pb 18 bits @@ -111,6 +65,10 @@ static wait_queue_head_t *gpio_wq; #endif +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif static int gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static ssize_t gpio_write(struct file * file, const char * buf, size_t count, @@ -148,55 +106,75 @@ #define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) #define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) unsigned long led_dummy; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read = 0; +#endif -static volatile unsigned long *data_out[NUM_PORTS] = { - GIO_REG_WR_ADDR(rw_pa_dout), - GIO_REG_WR_ADDR(rw_pb_dout), +static volatile unsigned long *data_out[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_dout), + GIO_REG_WR_ADDR(rw_pb_dout), &led_dummy, - GIO_REG_WR_ADDR(rw_pc_dout), - GIO_REG_WR_ADDR(rw_pd_dout), + GIO_REG_WR_ADDR(rw_pc_dout), + GIO_REG_WR_ADDR(rw_pd_dout), GIO_REG_WR_ADDR(rw_pe_dout), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; -static volatile unsigned long *data_in[NUM_PORTS] = { - GIO_REG_RD_ADDR(r_pa_din), - GIO_REG_RD_ADDR(r_pb_din), +static volatile unsigned long *data_in[NUM_PORTS] = { + GIO_REG_RD_ADDR(r_pa_din), + GIO_REG_RD_ADDR(r_pb_din), &led_dummy, - GIO_REG_RD_ADDR(r_pc_din), - GIO_REG_RD_ADDR(r_pd_din), + GIO_REG_RD_ADDR(r_pc_din), + GIO_REG_RD_ADDR(r_pd_din), GIO_REG_RD_ADDR(r_pe_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; -static unsigned long changeable_dir[NUM_PORTS] = { +static unsigned long changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, CONFIG_ETRAX_PB_CHANGEABLE_DIR, 0, CONFIG_ETRAX_PC_CHANGEABLE_DIR, - CONFIG_ETRAX_PD_CHANGEABLE_DIR, + CONFIG_ETRAX_PD_CHANGEABLE_DIR, CONFIG_ETRAX_PE_CHANGEABLE_DIR, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif }; -static unsigned long changeable_bits[NUM_PORTS] = { +static unsigned long changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, CONFIG_ETRAX_PB_CHANGEABLE_BITS, 0, CONFIG_ETRAX_PC_CHANGEABLE_BITS, CONFIG_ETRAX_PD_CHANGEABLE_BITS, CONFIG_ETRAX_PE_CHANGEABLE_BITS, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif }; -static volatile unsigned long *dir_oe[NUM_PORTS] = { - GIO_REG_WR_ADDR(rw_pa_oe), - GIO_REG_WR_ADDR(rw_pb_oe), +static volatile unsigned long *dir_oe[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_oe), + GIO_REG_WR_ADDR(rw_pb_oe), &led_dummy, - GIO_REG_WR_ADDR(rw_pc_oe), - GIO_REG_WR_ADDR(rw_pd_oe), + GIO_REG_WR_ADDR(rw_pc_oe), + GIO_REG_WR_ADDR(rw_pd_oe), GIO_REG_WR_ADDR(rw_pe_oe), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_rw_pv_oe, +#endif }; -static unsigned int +static unsigned int gpio_poll(struct file *file, poll_table *wait) { @@ -278,7 +256,7 @@ return 0; if ((data & priv->highalarm) || - (~data & priv->lowalarm)) { + (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } @@ -288,11 +266,26 @@ int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; + unsigned long flags; int ret = 0; + spin_lock_irqsave(&alarm_lock, flags); + priv = alarmlist; while (priv) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) { + data = (unsigned long)cached_virtual_gpio_read; + } + else { + data = *data_in[priv->minor]; + if (priv->minor == GPIO_MINOR_A) { + priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + } + } +#else data = *data_in[priv->minor]; +#endif if ((data & priv->highalarm) || (~data & priv->lowalarm)) { DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); @@ -301,11 +294,12 @@ } priv = priv->next; } + spin_unlock_irqrestore(&alarm_lock, flags); return ret; } -static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t +gpio_poll_timer_interrupt(int irq, void *dev_id) { if (gpio_some_alarms) { return IRQ_RETVAL(etrax_gpio_wake_up_check()); @@ -314,14 +308,17 @@ } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_pa_interrupt(int irq, void *dev_id) { reg_gio_rw_intr_mask intr_mask; reg_gio_r_masked_intr masked_intr; reg_gio_rw_ack_intr ack_intr; unsigned long tmp; unsigned long tmp2; - +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif + /* Find what PA interrupts are active */ masked_intr = REG_RD(gio, regi_gio, r_masked_intr); tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); @@ -331,6 +328,17 @@ tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); spin_unlock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + /* Ack them */ ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); @@ -339,13 +347,21 @@ intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) { + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + } +#endif intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); if (gpio_some_alarms) { return IRQ_RETVAL(etrax_gpio_wake_up_check()); } - return IRQ_NONE; + return IRQ_NONE; } @@ -358,8 +374,13 @@ unsigned long shadow; volatile unsigned long *port; ssize_t retval = count; - /* Only bits 0-7 may be used for write operations but allow all + /* Only bits 0-7 may be used for write operations but allow all devices except leds... */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) { + return -EFAULT; + } +#endif if (priv->minor == GPIO_MINOR_LEDS) { return -EFAULT; } @@ -416,25 +437,24 @@ static int gpio_open(struct inode *inode, struct file *filp) -{ +{ struct gpio_private *priv; int p = iminor(inode); if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), + priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; + memset(priv, 0, sizeof(*priv)); priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; priv->clk_mask = 0; priv->data_mask = 0; priv->highalarm = 0; @@ -443,20 +463,30 @@ filp->private_data = (void *)priv; + /* link it into our alarmlist */ + spin_lock_irq(&alarm_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&alarm_lock); + return 0; } static int gpio_release(struct inode *inode, struct file *filp) { - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; + struct gpio_private *p; + struct gpio_private *todel; /* local copies while updating them: */ unsigned long a_high, a_low; unsigned long some_alarms; /* unlink from alarmlist and free the private structure */ + spin_lock_irq(&alarm_lock); + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; + if (p == todel) { alarmlist = todel->next; } else { @@ -473,6 +503,9 @@ a_low = 0; while (p) { if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif a_high |= p->highalarm; a_low |= p->lowalarm; } @@ -483,23 +516,30 @@ p = p->next; } - spin_lock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variables 'some_alarms' and 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + some_alarms = 1; + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + gpio_some_alarms = some_alarms; gpio_pa_high_alarms = a_high; gpio_pa_low_alarms = a_low; - spin_unlock(&alarm_lock); + spin_unlock_irq(&alarm_lock); return 0; } -/* Main device API. ioctl's to read/set/clear bits, as well as to +/* Main device API. ioctl's to read/set/clear bits, as well as to * set alarms to wait for using a subsequent select(). */ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) { - /* Set direction 0=unchanged 1=input, - * return mask with 1=input + /* Set direction 0=unchanged 1=input, + * return mask with 1=input */ unsigned long flags; unsigned long dir_shadow; @@ -512,6 +552,10 @@ if (priv->minor == GPIO_MINOR_A) dir_shadow ^= 0xFF; /* Only 8 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif else dir_shadow ^= 0x3FFFF; /* Only 18 bits */ return dir_shadow; @@ -546,6 +590,11 @@ return -EINVAL; } +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif + switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ // read the port @@ -553,8 +602,6 @@ break; case IO_SETBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO SET 2\n"); // set changeable bits with a 1 in arg shadow = *data_out[priv->minor]; shadow |= (arg & changeable_bits[priv->minor]); @@ -563,8 +610,6 @@ break; case IO_CLRBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO CLR 2\n"); // clear changeable bits with a 1 in arg shadow = *data_out[priv->minor]; shadow &= ~(arg & changeable_bits[priv->minor]); @@ -574,52 +619,52 @@ case IO_HIGHALARM: // set alarm when bits with 1 in arg go high priv->highalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; if (priv->minor == GPIO_MINOR_A) { gpio_pa_high_alarms |= arg; } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_LOWALARM: // set alarm when bits with 1 in arg go low priv->lowalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; if (priv->minor == GPIO_MINOR_A) { gpio_pa_low_alarms |= arg; } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_CLRALARM: // clear alarm for bits with 1 in arg priv->highalarm &= ~arg; priv->lowalarm &= ~arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); if (priv->minor == GPIO_MINOR_A) { - if (gpio_pa_high_alarms & arg || + if (gpio_pa_high_alarms & arg || gpio_pa_low_alarms & arg) { /* Must update the gpio_pa_*alarms masks */ } } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ return *dir_oe[priv->minor]; case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ - /* Set direction 0=unchanged 1=input, - * return mask with 1=input + /* Set direction 0=unchanged 1=input, + * return mask with 1=input */ return setget_input(priv, arg); break; case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ - /* Set direction 0=unchanged 1=output, - * return mask with 1=output + /* Set direction 0=unchanged 1=output, + * return mask with 1=output */ return setget_output(priv, arg); - case IO_CFG_WRITE_MODE: + case IO_CFG_WRITE_MODE: { unsigned long dir_shadow; dir_shadow = *dir_oe[priv->minor]; @@ -641,7 +686,7 @@ } break; } - case IO_READ_INBITS: + case IO_READ_INBITS: /* *arg is result of reading the input pins */ val = *data_in[priv->minor]; if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) @@ -654,7 +699,7 @@ if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) return -EFAULT; break; - case IO_SETGET_INPUT: + case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ @@ -684,6 +729,132 @@ return 0; } +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int +virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + local_irq_save(flags); + // set changeable bits with a 1 in arg + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow |= (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); + // clear changeable bits with a 1 in arg + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow &= ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_HIGHALARM: + // set alarm when bits with 1 in arg go high + priv->highalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_LOWALARM: + // set alarm when bits with 1 in arg go low + priv->lowalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_CLRALARM: + // clear alarm for bits with 1 in arg + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + spin_lock(&alarm_lock); + spin_unlock(&alarm_lock); + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = *dir_oe[priv->minor]; + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) + { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read; + val &= ~*dir_oe[priv->minor]; + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= *dir_oe[priv->minor]; + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~*dir_oe[priv->minor]; + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) { @@ -714,6 +885,66 @@ .release = gpio_release, }; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void +virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + gpio_some_alarms = 1; +} +#endif /* main driver initialization routine, called from mem.c */ @@ -732,17 +963,18 @@ } /* Clear all leds */ - LED_NETWORK_SET(0); + LED_NETWORK_GRP0_SET(0); + LED_NETWORK_GRP1_SET(0); LED_ACTIVE_SET(0); LED_DISK_READ(0); LED_DISK_WRITE(0); - printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n"); + printk("ETRAX FS GPIO driver v2.5, (c) 2003-2006 Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. - */ + */ if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) { printk("err: timer0 irq for gpio\n"); @@ -757,6 +989,10 @@ intr_mask.gen_io = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif + return res; } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.c 2006-11-06 16:48:06.000000000 +0100 @@ -8,11 +8,11 @@ *! *! Nov 30 1998 Torbjorn Eliasson Initial version. *! Bjorn Wesen Elinux kernel version. -*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - +*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - *! don't use PB_I2C if DS1302 uses same bits, *! use PB. *| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now -*| generates nack on last received byte, +*| generates nack on last received byte, *| instead of ack. *| i2c_getack changed data level while clock *| was high, causing DS75 to see a stop condition @@ -22,7 +22,7 @@ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */ +/* $Id: i2c.c,v 1.6 2006/11/06 15:48:06 imres Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ #include @@ -60,8 +60,8 @@ #define I2C_DATA_HIGH 1 #define I2C_DATA_LOW 0 -#define i2c_enable() -#define i2c_disable() +#define i2c_enable() +#define i2c_disable() /* enable or disable output-enable, to select output or input on the i2c bus */ @@ -79,6 +79,8 @@ #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ + /****************** VARIABLE SECTION ************************************/ static struct crisv32_iopin cris_i2c_clk; @@ -154,7 +156,7 @@ } else { i2c_data(I2C_DATA_LOW); } - + i2c_delay(CLOCK_LOW_TIME/2); i2c_clk(I2C_CLOCK_HIGH); i2c_delay(CLOCK_HIGH_TIME); @@ -213,7 +215,7 @@ } i2c_clk(I2C_CLOCK_HIGH); i2c_delay(CLOCK_HIGH_TIME); - + /* * we leave the clock low, getbyte is usually followed * by sendack/nack, they assume the clock to be low @@ -252,6 +254,7 @@ * generate ACK clock pulse */ i2c_clk(I2C_CLOCK_HIGH); +#if 0 /* * Use PORT PB instead of I2C * for input. (I2C not working) @@ -264,6 +267,8 @@ i2c_data(1); i2c_disable(); i2c_dir_in(); +#endif + /* * now wait for ack */ @@ -285,13 +290,15 @@ * before we enable our output. If we keep data high * and enable output, we would generate a stop condition. */ +#if 0 i2c_data(I2C_DATA_LOW); - + /* * end clock pulse */ i2c_enable(); i2c_dir_out(); +#endif i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_HIGH_TIME/4); /* @@ -338,7 +345,7 @@ */ i2c_data(I2C_DATA_HIGH); i2c_delay(CLOCK_LOW_TIME); - + i2c_dir_in(); } @@ -369,24 +376,160 @@ i2c_delay(CLOCK_HIGH_TIME); i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_LOW_TIME); - + i2c_dir_in(); } /*#--------------------------------------------------------------------------- *# +*# FUNCTION NAME: i2c_write +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_write(unsigned char theSlave, void *data, size_t nbytes) +{ + int error, cntr = 3; + unsigned char bytes_wrote = 0; + unsigned char value; + unsigned long flags; + + spin_lock(&i2c_lock); + + do { + error = 0; + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * send data + */ + for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) { + memcpy(&value, data + bytes_wrote, sizeof value); + i2c_outbyte(value); + /* + * now it's time to wait for ack + */ + if (!i2c_getack()) + error |= 4; + } + /* + * end byte stream + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + spin_unlock(&i2c_lock); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_read +*# +*# DESCRIPTION : Reads a value from an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_read(unsigned char theSlave, void *data, size_t nbytes) +{ + unsigned char b = 0; + unsigned char bytes_read = 0; + int error, cntr = 3; + unsigned long flags; + + spin_lock(&i2c_lock); + + do { + error = 0; + memset(data, 0, nbytes); + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + /* + * generate start condition + */ + i2c_start(); + + /* + * send slave address + */ + i2c_outbyte((theSlave | 0x01)); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * fetch data + */ + for (bytes_read = 0; bytes_read < nbytes; bytes_read++) { + b = i2c_inbyte(); + memcpy(data + bytes_read, &b, sizeof b); + + if (bytes_read < (nbytes - 1)) { + i2c_sendack(); + } + } + /* + * last received byte needs to be nacked + * instead of acked + */ + i2c_sendnack(); + /* + * end sequence + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + spin_unlock(&i2c_lock); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# *# FUNCTION NAME: i2c_writereg *# *# DESCRIPTION : Writes a value to an I2C device *# *#--------------------------------------------------------------------------*/ int -i2c_writereg(unsigned char theSlave, unsigned char theReg, +i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue) { int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* @@ -431,10 +574,12 @@ * enable interrupt again */ local_irq_restore(flags); - + } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); + + spin_unlock(&i2c_lock); return -error; } @@ -453,6 +598,8 @@ int error, cntr = 3; unsigned long flags; + spin_lock(&i2c_lock); + do { error = 0; /* @@ -463,7 +610,7 @@ * generate start condition */ i2c_start(); - + /* * send slave address */ @@ -482,7 +629,7 @@ * now it's time to wait for ack */ if(!i2c_getack()) - error = 1; + error |= 2; /* * repeat start condition */ @@ -496,7 +643,7 @@ * wait for ack */ if(!i2c_getack()) - error = 1; + error |= 4; /* * fetch register */ @@ -514,9 +661,11 @@ * enable interrupt again */ local_irq_restore(flags); - + } while(error && cntr--); + spin_unlock(&i2c_lock); + return b; } @@ -546,7 +695,7 @@ switch (_IOC_NR(cmd)) { case I2C_WRITEREG: /* write to an i2c slave */ - D(printk("i2cw %d %d %d\n", + D(printk("i2cw %d %d %d\n", I2C_ARGSLAVE(arg), I2C_ARGREG(arg), I2C_ARGVALUE(arg))); @@ -558,18 +707,18 @@ { unsigned char val; /* read from an i2c slave */ - D(printk("i2cr %d %d ", + D(printk("i2cr %d %d ", I2C_ARGSLAVE(arg), I2C_ARGREG(arg))); val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg)); D(printk("= %d\n", val)); return val; - } + } default: return -EINVAL; } - + return 0; } @@ -583,28 +732,53 @@ int __init i2c_init(void) { - int res; + static int res = 0; + static int first = 1; + + if (!first) { + return res; + } + first = 0; - /* Setup and enable the Port B I2C interface */ + /* Setup and enable the DATA and CLK pins */ - crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); - crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + res = crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); + if (res < 0) { + return res; + } + + res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out); + + return res; +} - /* register char device */ +int __init +i2c_register(void) +{ + + int res; + + res = i2c_init(); + if (res < 0) { + return res; + } + + /* register char device */ res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); if(res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); - + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n"); + return 0; } /* this makes sure that i2c_init is called during boot */ -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.h linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.h --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.h 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.h 2006-11-06 16:48:06.000000000 +0100 @@ -3,6 +3,8 @@ /* High level I2C actions */ int __init i2c_init(void); +int i2c_write(unsigned char theSlave, void *data, size_t nbytes); +int i2c_read(unsigned char theSlave, void *data, size_t nbytes); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/iop_fw_load.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/iop_fw_load.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/iop_fw_load.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/iop_fw_load.c 2005-04-07 11:27:46.000000000 +0200 @@ -67,12 +67,12 @@ return -ENODEV; /* get firmware */ - retval = request_firmware(&fw_entry, - fw_name, + retval = request_firmware(&fw_entry, + fw_name, &iop_spu_device[spu_inst]); if (retval != 0) { - printk(KERN_ERR + printk(KERN_ERR "iop_load_spu: Failed to load firmware \"%s\"\n", fw_name); return retval; @@ -123,7 +123,7 @@ return retval; } -int iop_fw_load_mpu(unsigned char *fw_name) +int iop_fw_load_mpu(unsigned char *fw_name) { const unsigned int start_addr = 0; reg_iop_mpu_rw_ctrl mpu_ctrl; @@ -135,13 +135,13 @@ retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device); if (retval != 0) { - printk(KERN_ERR + printk(KERN_ERR "iop_load_spu: Failed to load firmware \"%s\"\n", fw_name); return retval; } data = (u32 *) fw_entry->data; - + /* disable MPU */ mpu_ctrl.en = regk_iop_mpu_no; REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/nandflash.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/nandflash.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/nandflash.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/nandflash.c 2006-10-16 14:56:46.000000000 +0200 @@ -5,8 +5,8 @@ * * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $ + * + * $Id: nandflash.c,v 1.8 2006/10/16 12:56:46 ricardw Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -32,37 +32,53 @@ #define ALE_BIT 6 #define BY_BIT 7 +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +/* Bitmask for mtd nand control bits */ +#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE) + + static struct mtd_info *crisv32_mtd = NULL; -/* +/* * hardware specific access to control-lines */ -static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { unsigned long flags; - reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + reg_gio_rw_pa_dout dout; + struct nand_chip *this = mtd->priv; local_irq_save(flags); - switch(cmd){ - case NAND_CTL_SETCLE: - dout.data |= (1<IO_ADDR_W); + local_irq_restore(flags); } @@ -129,26 +145,26 @@ /* Set address of NAND IO lines */ this->IO_ADDR_R = read_cs; this->IO_ADDR_W = write_cs; - this->hwcontrol = crisv32_hwcontrol; + this->cmd_ctrl = crisv32_hwcontrol; this->dev_ready = crisv32_device_ready; /* 20 us command delay time */ - this->chip_delay = 20; - this->eccmode = NAND_ECC_SOFT; + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - this->options = NAND_USE_FLASH_BBT; + /* this->options = NAND_USE_FLASH_BBT; */ /* Scan to find existance of the device */ if (nand_scan (crisv32_mtd, 1)) { err = -ENXIO; goto out_ior; } - + return crisv32_mtd; - + out_ior: iounmap((void *)read_cs); - iounmap((void *)write_cs); + iounmap((void *)write_cs); out_mtd: kfree (crisv32_mtd); return NULL; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pcf8563.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pcf8563.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pcf8563.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pcf8563.c 2006-10-27 17:22:13.000000000 +0200 @@ -10,7 +10,7 @@ * 400 kbits/s. The built-in word address register is incremented * automatically after each written or read byte. * - * Copyright (c) 2002-2003, Axis Communications AB + * Copyright (c) 2002-2006, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg . @@ -37,24 +37,27 @@ #define PCF8563_MAJOR 121 /* Local major number. */ #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.1 $" +#define DRIVER_VERSION "$Revision: 1.9 $" /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) +static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -int pcf8563_open(struct inode *, struct file *); -int pcf8563_release(struct inode *, struct file *); + +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low = 0; static struct file_operations pcf8563_fops = { owner: THIS_MODULE, ioctl: pcf8563_ioctl, - open: pcf8563_open, - release: pcf8563_release, }; unsigned char @@ -62,7 +65,7 @@ { unsigned char res = rtc_read(reg); - /* The PCF8563 does not return 0 for unimplemented bits */ + /* The PCF8563 does not return 0 for unimplemented bits. */ switch (reg) { case RTC_SECONDS: case RTC_MINUTES: @@ -95,11 +98,6 @@ void pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } @@ -114,11 +112,13 @@ tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) + if (tm->tm_sec & 0x80) { printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); tm->tm_sec &= 0x7F; tm->tm_min &= 0x7F; tm->tm_hour &= 0x3F; @@ -137,8 +137,20 @@ int __init pcf8563_init(void) { + static int res = 0; + static int first = 1; + + if (!first) { + return res; + } + first = 0; + /* Initiate the i2c protocol. */ - i2c_init(); + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; + } /* * First of all we need to reset the chip. This is done by @@ -170,31 +182,28 @@ if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); - return -1; + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - - return 0; + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit pcf8563_exit(void) { if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { - printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); + printk(KERN_INFO "%s: Unable to unregister device.\n", + PCF8563_NAME); } } @@ -203,7 +212,8 @@ * POSIX says so! */ int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -217,31 +227,35 @@ { struct rtc_time tm; - memset(&tm, 0, sizeof (struct rtc_time)); + spin_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); get_rtc_time(&tm); - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); return -EFAULT; } + spin_unlock(&rtc_lock); + return 0; } - case RTC_SET_TIME: { -#ifdef CONFIG_ETRAX_RTC_READONLY - return -EPERM; -#else int leap; int year; int century; struct rtc_time tm; + memset(&tm, 0, sizeof tm); if (!capable(CAP_SYS_TIME)) return -EPERM; - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + if (copy_from_user(&tm, (struct rtc_time *) arg, + sizeof tm)) { return -EFAULT; + } /* Convert from struct tm to struct rtc_time. */ tm.tm_year += 1900; @@ -253,7 +267,8 @@ * that years divisible by 400 _are_ leap years. */ year = tm.tm_year; - leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); /* Perform some sanity checks. */ if ((tm.tm_year < 1970) || @@ -263,19 +278,23 @@ (tm.tm_wday >= 7) || (tm.tm_hour >= 24) || (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) + (tm.tm_sec >= 60)) { return -EINVAL; + } century = (tm.tm_year >= 2000) ? 0x80 : 0; tm.tm_year = tm.tm_year % 100; BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_hour); BIN_TO_BCD(tm.tm_min); BIN_TO_BCD(tm.tm_sec); tm.tm_mon |= century; + spin_lock(&rtc_lock); + rtc_write(RTC_YEAR, tm.tm_year); rtc_write(RTC_MONTH, tm.tm_mon); rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ @@ -284,36 +303,40 @@ rtc_write(RTC_MINUTES, tm.tm_min); rtc_write(RTC_SECONDS, tm.tm_sec); + spin_unlock(&rtc_lock); + return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ } - case RTC_VLOW_RD: - { - int vl_bit = 0; - - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) { + return -EFAULT; + } + return 0; - } case RTC_VLOW_SET: { - /* Clear the VL bit in the seconds register */ + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ + int ret = rtc_read(RTC_SECONDS); rtc_write(RTC_SECONDS, (ret & 0x7F)); + /* Clear the cached value. */ + voltage_low = 0; + return 0; } - default: return -ENOTTY; } @@ -321,17 +344,32 @@ return 0; } -int -pcf8563_open(struct inode *inode, struct file *filp) +static int __init +pcf8563_register(void) { - return 0; -} + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC " + "device.\n", PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } -int -pcf8563_release(struct inode *inode, struct file *filp) -{ return 0; } -module_init(pcf8563_init); +module_init(pcf8563_register); module_exit(pcf8563_exit); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/bios.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/bios.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/bios.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/bios.c 2006-10-13 14:43:15.000000000 +0200 @@ -60,7 +60,7 @@ u16 cmd, old_cmd; int idx; struct resource *r; - + pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; for(idx=0; idx<6; idx++) { diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/dma.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/dma.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/dma.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/dma.c 2005-10-31 09:48:04.000000000 +0100 @@ -62,7 +62,7 @@ { struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; int order = get_order(size); - + if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; @@ -120,7 +120,7 @@ void dma_release_declared_memory(struct device *dev) { struct dma_coherent_mem *mem = dev->dma_mem; - + if(!mem) return; dev->dma_mem = NULL; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/sync_serial.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/sync_serial.c --- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/sync_serial.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/sync_serial.c 2007-01-09 10:29:20.000000000 +0100 @@ -50,7 +50,7 @@ /* readp writep */ /* */ /* If the application keeps up the pace readp will be right after writep.*/ -/* If the application can't keep the pace we have to throw away data. */ +/* If the application can't keep the pace we have to throw away data. */ /* The idea is that readp should be ready with the data pointed out by */ /* Descr[i] when the DMA has filled in Descr[i+1]. */ /* Otherwise we will discard */ @@ -65,6 +65,7 @@ #define IN_DESCR_SIZE 256 #define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) #define OUT_BUFFER_SIZE 4096 +#define NUM_OUT_DESCRS 4 #define DEFAULT_FRAME_RATE 0 #define DEFAULT_WORD_RATE 7 @@ -112,7 +113,7 @@ dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); dma_descr_context in_context __attribute__ ((__aligned__(32))); - dma_descr_data out_descr __attribute__ ((__aligned__(16))); + dma_descr_data out_descr[NUM_OUT_DESCRS] __attribute__ ((__aligned__(16))); dma_descr_context out_context __attribute__ ((__aligned__(32))); wait_queue_head_t out_wait_q; wait_queue_head_t in_wait_q; @@ -130,9 +131,9 @@ static int sync_serial_ioctl(struct inode*, struct file*, unsigned int cmd, unsigned long arg); -static ssize_t sync_serial_write(struct file * file, const char * buf, +static ssize_t sync_serial_write(struct file * file, const char * buf, size_t count, loff_t *ppos); -static ssize_t sync_serial_read(struct file *file, char *buf, +static ssize_t sync_serial_read(struct file *file, char *buf, size_t count, loff_t *ppos); #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ @@ -146,8 +147,8 @@ static void start_dma(struct sync_port *port, const char* data, int count); static void start_dma_in(sync_port* port); #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); #endif #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ @@ -157,7 +158,7 @@ #define SYNC_SER_MANUAL #endif #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t manual_interrupt(int irq, void *dev_id); #endif /* The ports */ @@ -201,8 +202,8 @@ { ports[0].enabled = 0; ports[1].enabled = 0; - - if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) + + if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) { printk("unable to get major for synchronous serial port\n"); return -EBUSY; @@ -243,13 +244,13 @@ DEBUG(printk("Init sync serial port %d\n", portnbr)); - port->port_nbr = portnbr; + port->port_nbr = portnbr; port->init_irqs = 1; port->outp = port->out_buffer; port->output = 1; port->input = 0; - + port->readp = port->flip; port->writep = port->flip; port->in_buffer_size = IN_BUFFER_SIZE; @@ -286,11 +287,16 @@ tr_cfg.sample_size = 7; tr_cfg.sh_dir = regk_sser_msbfirst; tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; +#if 0 tr_cfg.rate_ctrl = regk_sser_bulk; tr_cfg.data_pin_use = regk_sser_dout; +#else + tr_cfg.rate_ctrl = regk_sser_iso; + tr_cfg.data_pin_use = regk_sser_dout; +#endif tr_cfg.bulk_wspace = 1; REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); - + rec_cfg.sample_size = 7; rec_cfg.sh_dir = regk_sser_msbfirst; rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; @@ -303,17 +309,17 @@ int avail; unsigned char *start; unsigned char *end; - + start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ /* 0123456789 0123456789 * ----- - ----- * ^rp ^wp ^wp ^rp */ - + if (end >= start) avail = end - start; - else + else avail = port->in_buffer_size - (start - end); return avail; } @@ -323,17 +329,17 @@ int avail; unsigned char *start; unsigned char *end; - + start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ /* 0123456789 0123456789 * ----- ----- * ^rp ^wp ^wp ^rp */ - + if (end >= start) avail = end - start; - else + else avail = port->flip + port->in_buffer_size - start; return avail; } @@ -343,10 +349,10 @@ int dev = iminor(inode); sync_port* port; reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; - reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; - - DEBUG(printk("Open sync serial port %d\n", dev)); + reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; + DEBUG(printk("Open sync serial port %d\n", dev)); + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); @@ -354,7 +360,7 @@ } port = &ports[dev]; /* Allow open this device twice (assuming one reader and one writer) */ - if (port->busy == 2) + if (port->busy == 2) { DEBUG(printk("Device is busy.. \n")); return -EBUSY; @@ -422,8 +428,8 @@ DMA_VERBOSE_ON_ERROR, 0, dma_sser1)) { - free_irq(21, &ports[1]); - free_irq(20, &ports[1]); + free_irq(DMA6_INTR_VECT, &ports[1]); + free_irq(DMA7_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); return -EBUSY; } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR, @@ -446,7 +452,7 @@ /* Enable DMA IRQs */ REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask); REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask); - /* Set up wordsize = 2 for DMAs. */ + /* Set up wordsize = 1 for DMAs. */ DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1); DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1); @@ -497,7 +503,7 @@ port = &ports[dev]; if (port->busy) port->busy--; - if (!port->busy) + if (!port->busy) /* XXX */ ; return 0; } @@ -508,17 +514,29 @@ unsigned int mask = 0; sync_port* port; DEBUGPOLL( static unsigned int prev_mask = 0; ); - + port = &ports[dev]; + + if (!port->started) + { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + rec_cfg.rec_en = port->input; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + poll_wait(file, &port->out_wait_q, wait); poll_wait(file, &port->in_wait_q, wait); /* Some room to write */ - if (port->out_count < OUT_BUFFER_SIZE) + if (port->output && port->out_count < OUT_BUFFER_SIZE) mask |= POLLOUT | POLLWRNORM; /* At least an inbufchunk of data */ - if (sync_data_avail(port) >= port->inbufchunk) + if (port->input && sync_data_avail(port) >= port->inbufchunk) mask |= POLLIN | POLLRDNORM; - + DEBUGPOLL(if (mask != prev_mask) printk("sync_serial_poll: mask 0x%08X %s %s\n", mask, mask&POLLOUT?"POLLOUT":"", mask&POLLIN?"POLLIN":""); @@ -531,14 +549,15 @@ unsigned int cmd, unsigned long arg) { int return_val = 0; + int dma_w_size = regk_dma_set_w_size1; int dev = iminor(file->f_dentry->d_inode); sync_port* port; reg_sser_rw_tr_cfg tr_cfg; reg_sser_rw_rec_cfg rec_cfg; - reg_sser_rw_frm_cfg frm_cfg; + reg_sser_rw_frm_cfg frm_cfg; reg_sser_rw_cfg gen_cfg; reg_sser_rw_intr_mask intr_mask; - + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); @@ -558,9 +577,32 @@ case SSP_SPEED: if (GET_SPEED(arg) == CODEC) { + unsigned int freq; + gen_cfg.base_freq = regk_sser_f32; - /* FREQ = 0 => 4 MHz => clk_div = 7*/ - gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg)); + + /* Clock divider will internally be + * gen_cfg.clk_div + 1. + */ + + freq = GET_FREQ(arg); + switch (freq) + { + case FREQ_32kHz: + case FREQ_64kHz: + case FREQ_128kHz: + case FREQ_256kHz: + gen_cfg.clk_div = 125 * (1 << (freq - FREQ_256kHz)) - 1; + break; + case FREQ_512kHz: + gen_cfg.clk_div = 62; + break; + case FREQ_1MHz: + case FREQ_2MHz: + case FREQ_4MHz: + gen_cfg.clk_div = 8 * (1 << freq) - 1; + break; + } } else { @@ -625,87 +667,118 @@ case MASTER_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.out_on = regk_sser_tr; + frm_cfg.frame_pin_dir = regk_sser_out; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; default: spin_unlock_irq(&port->lock); return -EINVAL; - + } if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)) intr_mask.rdav = regk_sser_yes; break; case SSP_FRAME_SYNC: - if (arg & NORMAL_SYNC) + if (arg & NORMAL_SYNC) { + frm_cfg.rec_delay = 1; frm_cfg.tr_delay = 1; + } else if (arg & EARLY_SYNC) - frm_cfg.tr_delay = 0; + frm_cfg.rec_delay = frm_cfg.tr_delay = 0; + else if (arg & SECOND_WORD_SYNC) { + frm_cfg.rec_delay = 17; + frm_cfg.tr_delay = 1; + } + + tr_cfg.bulk_wspace = frm_cfg.tr_delay; frm_cfg.early_wend = regk_sser_yes; - if (arg & BIT_SYNC) + if (arg & BIT_SYNC) frm_cfg.type = regk_sser_edge; else if (arg & WORD_SYNC) frm_cfg.type = regk_sser_level; else if (arg & EXTENDED_SYNC) frm_cfg.early_wend = regk_sser_no; - + if (arg & SYNC_ON) frm_cfg.frame_pin_use = regk_sser_frm; else if (arg & SYNC_OFF) frm_cfg.frame_pin_use = regk_sser_gio0; - - if (arg & WORD_SIZE_8) + + if (arg & WORD_SIZE_8) { rec_cfg.sample_size = tr_cfg.sample_size = 7; - else if (arg & WORD_SIZE_12) + dma_w_size = regk_dma_set_w_size1; + } + else if (arg & WORD_SIZE_12) { rec_cfg.sample_size = tr_cfg.sample_size = 11; - else if (arg & WORD_SIZE_16) + dma_w_size = regk_dma_set_w_size2; + } + else if (arg & WORD_SIZE_16) { rec_cfg.sample_size = tr_cfg.sample_size = 15; - else if (arg & WORD_SIZE_24) + dma_w_size = regk_dma_set_w_size2; + } + else if (arg & WORD_SIZE_24) { rec_cfg.sample_size = tr_cfg.sample_size = 23; - else if (arg & WORD_SIZE_32) + dma_w_size = regk_dma_set_w_size2; + } + else if (arg & WORD_SIZE_32) { rec_cfg.sample_size = tr_cfg.sample_size = 31; + dma_w_size = regk_dma_set_w_size2; + } if (arg & BIT_ORDER_MSB) rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_msbfirst; else if (arg & BIT_ORDER_LSB) rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst; - - if (arg & FLOW_CONTROL_ENABLE) + + if (arg & FLOW_CONTROL_ENABLE) { + frm_cfg.status_pin_use = regk_sser_frm; rec_cfg.fifo_thr = regk_sser_thr16; - else if (arg & FLOW_CONTROL_DISABLE) + } + else if (arg & FLOW_CONTROL_DISABLE) { + frm_cfg.status_pin_use = regk_sser_gio0; rec_cfg.fifo_thr = regk_sser_inf; + } if (arg & CLOCK_NOT_GATED) gen_cfg.gate_clk = regk_sser_no; else if (arg & CLOCK_GATED) gen_cfg.gate_clk = regk_sser_yes; - + break; case SSP_IPOLARITY: /* NOTE!! negedge is considered NORMAL */ @@ -713,12 +786,12 @@ rec_cfg.clk_pol = regk_sser_neg; else if (arg & CLOCK_INVERT) rec_cfg.clk_pol = regk_sser_pos; - + if (arg & FRAME_NORMAL) frm_cfg.level = regk_sser_pos_hi; else if (arg & FRAME_INVERT) frm_cfg.level = regk_sser_neg_lo; - + if (arg & STATUS_NORMAL) gen_cfg.hold_pol = regk_sser_pos; else if (arg & STATUS_INVERT) @@ -726,15 +799,15 @@ break; case SSP_OPOLARITY: if (arg & CLOCK_NORMAL) - gen_cfg.out_clk_pol = regk_sser_neg; - else if (arg & CLOCK_INVERT) gen_cfg.out_clk_pol = regk_sser_pos; - + else if (arg & CLOCK_INVERT) + gen_cfg.out_clk_pol = regk_sser_neg; + if (arg & FRAME_NORMAL) frm_cfg.level = regk_sser_pos_hi; else if (arg & FRAME_INVERT) frm_cfg.level = regk_sser_neg_lo; - + if (arg & STATUS_NORMAL) gen_cfg.hold_pol = regk_sser_pos; else if (arg & STATUS_INVERT) @@ -772,9 +845,10 @@ if (port->started) { - tr_cfg.tr_en = port->output; rec_cfg.rec_en = port->input; + gen_cfg.en = (port->output | port->input); } + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); @@ -782,11 +856,24 @@ REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + + if (cmd == SSP_FRAME_SYNC && + (arg & (WORD_SIZE_8 | WORD_SIZE_12 | WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) { + int en = gen_cfg.en; + gen_cfg.en = 0; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + /* ##### Should DMA be stoped before we change dma size? */ + DMA_WR_CMD (port->regi_dmain, dma_w_size); + DMA_WR_CMD (port->regi_dmaout, dma_w_size); + gen_cfg.en = en; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + } + spin_unlock_irq(&port->lock); return return_val; } -static ssize_t sync_serial_write(struct file * file, const char * buf, +static ssize_t sync_serial_write(struct file * file, const char * buf, size_t count, loff_t *ppos) { int dev = iminor(file->f_dentry->d_inode); @@ -807,7 +894,7 @@ DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); /* Space to end of buffer */ - /* + /* * out_buffer 012345<- c ->OUT_BUFFER_SIZE * outp^ +out_count ^free_outp @@ -824,7 +911,7 @@ free_outp = outp + port->out_count; spin_unlock_irqrestore(&port->lock, flags); out_buffer = (unsigned long)port->out_buffer; - + /* Find out where and how much to write */ if (free_outp >= out_buffer + OUT_BUFFER_SIZE) free_outp -= OUT_BUFFER_SIZE; @@ -834,7 +921,7 @@ c = outp - free_outp; if (c > count) c = count; - + // DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); if (copy_from_user((void*)free_outp, buf, c)) return -EFAULT; @@ -854,13 +941,10 @@ if (!port->started) { reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); - reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); cfg.en = regk_sser_yes; - tr_cfg.tr_en = port->output; rec_cfg.rec_en = port->input; REG_WR(sser, port->regi_sser, rw_cfg, cfg); - REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); port->started = 1; } @@ -887,7 +971,7 @@ } /* Sleep until all sent */ - + add_wait_queue(&port->out_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&port->lock, flags); @@ -916,13 +1000,13 @@ return count; } -static ssize_t sync_serial_read(struct file * file, char * buf, +static ssize_t sync_serial_read(struct file * file, char * buf, size_t count, loff_t *ppos) { int dev = iminor(file->f_dentry->d_inode); int avail; sync_port *port; - unsigned char* start; + unsigned char* start; unsigned char* end; unsigned long flags; @@ -949,7 +1033,7 @@ port->started = 1; } - + /* Calculate number of available bytes */ /* Save pointers to avoid that they are modified by interrupt */ spin_lock_irqsave(&port->lock, flags); @@ -958,11 +1042,12 @@ spin_unlock_irqrestore(&port->lock, flags); while ((start == end) && !port->full) /* No data */ { + DEBUGREAD(printk("&")); if (file->f_flags & O_NONBLOCK) - { + { return -EAGAIN; } - + interruptible_sleep_on(&port->in_wait_q); if (signal_pending(current)) { @@ -979,9 +1064,9 @@ avail = port->in_buffer_size; else if (end > start) avail = end - start; - else + else avail = port->flip + port->in_buffer_size - start; - + count = count > avail ? avail : count; if (copy_to_user(buf, start, count)) return -EFAULT; @@ -1016,7 +1101,7 @@ data |= *port->outp++; port->out_count-=2; tr_data.data = data; - REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) port->outp = port->out_buffer; } @@ -1032,7 +1117,7 @@ case 24: port->out_count-=3; tr_data.data = *(unsigned short *)port->outp; - REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); port->outp+=2; tr_data.data = *port->outp++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); @@ -1042,10 +1127,10 @@ case 32: port->out_count-=4; tr_data.data = *(unsigned short *)port->outp; - REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); port->outp+=2; tr_data.data = *(unsigned short *)port->outp; - REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); + REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); port->outp+=2; if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) port->outp = port->out_buffer; @@ -1056,15 +1141,27 @@ static void start_dma(struct sync_port* port, const char* data, int count) { + int i; + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); port->tr_running = 1; - port->out_descr.buf = (char*)virt_to_phys((char*)data); - port->out_descr.after = port->out_descr.buf + count; - port->out_descr.eol = port->out_descr.intr = 1; + for (i = 0; i < NUM_OUT_DESCRS; i++) + { + port->out_descr[i].buf = (char*)virt_to_phys(port->out_buffer + 1024*i); + port->out_descr[i].after = port->out_descr[i].buf + 1024; + port->out_descr[i].eol = 0; + port->out_descr[i].intr = 1; + port->out_descr[i].next = virt_to_phys(&port->out_descr[i+1]); + } + port->out_descr[i-1].next = virt_to_phys(&port->out_descr[0]); - port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr); - port->out_context.saved_data_buf = port->out_descr.buf; + port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr[0]); + port->out_context.saved_data_buf = port->out_descr[0].buf; DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context)); + + tr_cfg.tr_en = regk_sser_yes; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); } @@ -1073,7 +1170,7 @@ int i; char* buf; port->writep = port->flip; - + if (port->writep > port->flip + port->in_buffer_size) { panic("Offset too large in sync serial driver\n"); @@ -1099,7 +1196,7 @@ } #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t tr_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; @@ -1108,7 +1205,7 @@ unsigned int sentl; int found = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NUMBER_OF_PORTS; i++) { sync_port *port = &ports[i]; if (!port->enabled || !port->use_dma ) @@ -1133,18 +1230,21 @@ if (c > port->out_count) c = port->out_count; DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); - start_dma(port, port->outp, c); + //start_dma(port, port->outp, c); } else { - DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); + DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); port->tr_running = 0; + tr_cfg.tr_en = regk_sser_no; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); } wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ - } + } } return IRQ_RETVAL(found); } /* tr_interrupt */ -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t rx_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; @@ -1152,7 +1252,7 @@ int i; int found = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NUMBER_OF_PORTS; i++) { sync_port *port = &ports[i]; @@ -1164,17 +1264,17 @@ if (masked.data) /* Descriptor interrupt */ { found = 1; - while (REG_RD(dma, port->regi_dmain, rw_data) != + while (REG_RD(dma, port->regi_dmain, rw_data) != virt_to_phys(port->next_rx_desc)) { - + DEBUGRXINT(printk("!")); if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { int first_size = port->flip + port->in_buffer_size - port->writep; memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size); memcpy(port->flip, phys_to_virt((unsigned)port->next_rx_desc->buf+first_size), port->inbufchunk - first_size); port->writep = port->flip + port->inbufchunk - first_size; } else { - memcpy((char*)port->writep, - phys_to_virt((unsigned)port->next_rx_desc->buf), + memcpy((char*)port->writep, + phys_to_virt((unsigned)port->next_rx_desc->buf), port->inbufchunk); port->writep += port->inbufchunk; if (port->writep >= port->flip + port->in_buffer_size) @@ -1184,11 +1284,13 @@ { port->full = 1; } - - port->next_rx_desc->eol = 0; - port->prev_rx_desc->eol = 1; - port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + + port->next_rx_desc->eol = 1; + port->prev_rx_desc->eol = 0; + flush_dma_descr(port->prev_rx_desc,0); // Cache bug workaround + port->prev_rx_desc = port->next_rx_desc; port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); + flush_dma_descr(port->prev_rx_desc,1); // Cache bug workaround wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ DMA_CONTINUE(port->regi_dmain); REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr); @@ -1201,7 +1303,7 @@ #endif /* SYNC_SER_DMA */ #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t manual_interrupt(int irq, void *dev_id) { int i; int found = 0; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/Makefile 2006-12-06 14:17:02.000000000 +0100 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $ +# $Id: Makefile,v 1.13 2006/12/06 13:17:02 starvik Exp $ # # Makefile for the linux kernel. # @@ -8,7 +8,7 @@ obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \ process.o ptrace.o setup.o signal.o traps.o time.o \ - arbiter.o io.o + arbiter.o io.o cache.o cacheflush.o obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o @@ -16,6 +16,7 @@ obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o obj-$(CONFIG_MODULES) += crisksyms.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o clean: diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/arbiter.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/arbiter.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/arbiter.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/arbiter.c 2006-10-13 14:43:13.000000000 +0200 @@ -6,7 +6,7 @@ * bandwidth (e.g. ethernet) and then the remaining slots are divided * on all the active clients. * - * Copyright (c) 2004, 2005 Axis Communications AB. + * Copyright (c) 2004, 2005, 2006 Axis Communications AB. */ #include @@ -44,35 +44,88 @@ {regi_marb_bp3} }; -static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static u8 active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; DEFINE_SPINLOCK(arbiter_lock); -static irqreturn_t +static irqreturn_t crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); -static void crisv32_arbiter_config(int region) +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int region, int unused_slots) { int slot; int client; int interval = 0; - int val[NBR_OF_SLOTS]; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; for (slot = 0; slot < NBR_OF_SLOTS; slot++) - val[slot] = NBR_OF_CLIENTS + 1; + val[slot] = -1; for (client = 0; client < NBR_OF_CLIENTS; client++) { int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ if (!requested_slots[region][client]) - continue; - interval = NBR_OF_SLOTS / requested_slots[region][client]; + { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!active_clients[region][client] || unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } + else + interval = NBR_OF_SLOTS / requested_slots[region][client]; + pos = 0; while (pos < NBR_OF_SLOTS) { - if (val[pos] != NBR_OF_CLIENTS + 1) + if (val[pos] >= 0) pos++; else { @@ -85,7 +138,13 @@ client = 0; for (slot = 0; slot < NBR_OF_SLOTS; slot++) { - if (val[slot] == NBR_OF_CLIENTS + 1) + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { int first = client; while(!active_clients[region][client]) { @@ -100,7 +159,7 @@ REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); else if (region == INT_REGION) REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); - } + } } extern char _stext, _etext; @@ -111,18 +170,28 @@ if (initialized) return; - + initialized = 1; - /* CPU caches are active. */ - active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; - crisv32_arbiter_config(EXT_REGION); - crisv32_arbiter_config(INT_REGION); + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; + crisv32_arbiter_config(EXT_REGION, 0); + crisv32_arbiter_config(INT_REGION, 0); if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, "arbiter", NULL)) printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); - + #ifndef CONFIG_ETRAX_KGDB /* Global watch for writes to kernel text segment. */ crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, @@ -130,6 +199,7 @@ #endif } +/* Main entry for bandwidth allocation. */ int crisv32_arbiter_allocate_bandwidth(int client, int region, @@ -141,39 +211,76 @@ int req; crisv32_arbiter_init(); - + for (i = 0; i < NBR_OF_CLIENTS; i++) { total_assigned += requested_slots[region][i]; total_clients += active_clients[region][i]; } - req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); - if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) return -ENOMEM; active_clients[region][client] = 1; requested_slots[region][client] = req; - crisv32_arbiter_config(region); + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); return 0; } +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + + requested_slots[region][client] = 0; + active_clients[region][client] = 0; + + for (i = 0; i < NBR_OF_CLIENTS; i++) + total_assigned += requested_slots[region][i]; + + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); +} + int crisv32_arbiter_watch(unsigned long start, unsigned long size, unsigned long clients, unsigned long accesses, watch_callback* cb) { int i; - + crisv32_arbiter_init(); - + if (start > 0x80000000) { printk("Arbiter: %lX doesn't look like a physical address", start); return -EFAULT; } spin_lock(&arbiter_lock); - + for (i = 0; i < NUMBER_OF_BP; i++) { if (!watches[i].used) { reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); @@ -214,7 +321,7 @@ crisv32_arbiter_init(); spin_lock(&arbiter_lock); - + if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { spin_unlock(&arbiter_lock); return -EINVAL; @@ -239,7 +346,7 @@ extern void show_registers(struct pt_regs *regs); -static irqreturn_t +static irqreturn_t crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) { reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); @@ -248,10 +355,10 @@ reg_marb_bp_r_brk_op r_op; reg_marb_bp_r_brk_first_client r_first; reg_marb_bp_r_brk_size r_size; - reg_marb_bp_rw_ack ack = {0}; + reg_marb_bp_rw_ack ack = {0}; reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; struct crisv32_watch_entry* watch; - + if (masked_intr.bp0) { watch = &watches[0]; ack_intr.bp0 = regk_marb_yes; @@ -291,6 +398,6 @@ if (watch->cb) watch->cb(); - + return IRQ_HANDLED; } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/asm-offsets.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/asm-offsets.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/asm-offsets.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/asm-offsets.c 2003-06-02 10:39:38.000000000 +0200 @@ -16,8 +16,8 @@ { #define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry)) ENTRY(orig_r10); - ENTRY(r13); - ENTRY(r12); + ENTRY(r13); + ENTRY(r12); ENTRY(r11); ENTRY(r10); ENTRY(r9); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cache.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cache.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cache.c 2007-01-23 13:09:57.000000000 +0100 @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +// This file is used to workaround a cache bug, Guinness TR 106 + +inline void flush_dma_descr(dma_descr_data* descr, int flush_buf) +{ + // Flush descriptor to make sure we get correct in_eop and after + asm volatile ("ftagd [%0]" :: "r" (descr)); + // Flush buffer pointed out by descriptor + if (flush_buf) + cris_flush_cache_range(phys_to_virt((unsigned)descr->buf), (unsigned)(descr->after - descr->buf)); +} + +void flush_dma_list(dma_descr_data* descr) +{ + while(1) + { + flush_dma_descr(descr, 1); + if (descr->eol) + break; + descr = phys_to_virt((unsigned)descr->next); + } +} + +EXPORT_SYMBOL(flush_dma_list); +EXPORT_SYMBOL(flush_dma_descr); +EXPORT_SYMBOL(cris_flush_cache); +EXPORT_SYMBOL(cris_flush_cache_range); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cacheflush.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cacheflush.S --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cacheflush.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cacheflush.S 2006-12-06 14:17:02.000000000 +0100 @@ -0,0 +1,93 @@ + .global cris_flush_cache_range +cris_flush_cache_range: + move.d 1024, $r12 + cmp.d $r11, $r12 + bhi cris_flush_1KB + nop + add.d $r10, $r11 +cris_flush_last: + addq 32, $r10 + cmp.d $r11, $r10 + blt cris_flush_last + ftagd [$r10] + ret + nop +cris_flush_1KB: + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ftagd [$r10] + addq 32, $r10 + ba cris_flush_cache_range + sub.d $r12, $r11 + + .global cris_flush_cache +cris_flush_cache: + moveq 0, $r10 +cris_flush_line: + move.d 16*1024, $r11 + addq 16, $r10 + cmp.d $r10, $r11 + blt cris_flush_line + fidxd [$r10] + ret + nop diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cpufreq.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cpufreq.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cpufreq.c 2006-11-03 11:35:52.000000000 +0100 @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state (unsigned int state) +{ + int i; + struct cpufreq_freqs freqs; + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + + for_each_cpu(i) { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + // Even though we may be SMP they will share the same clock + // so all settings are made on CPU0. + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify (struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target(policy, cris_freq_table, target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + + +static struct freq_attr* cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_bif_core_rw_sdram_timing timing = + REG_RD(bif_core, regi_bif_core, rw_sdram_timing); + timing.cpd = (freqs->new == 200000 ? 0 : 1); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++); + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + + +module_init(cris_freq_init); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/crisksyms.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/crisksyms.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/crisksyms.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/crisksyms.c 2006-11-21 04:21:34.000000000 +0100 @@ -3,6 +3,7 @@ #include #include #include +#include /* Functions for allocating DMA channels */ EXPORT_SYMBOL(crisv32_request_dma); @@ -16,7 +17,11 @@ /* Functions for handling pinmux */ EXPORT_SYMBOL(crisv32_pinmux_alloc); +EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed); EXPORT_SYMBOL(crisv32_pinmux_dealloc); +EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed); +EXPORT_SYMBOL(crisv32_io_get_name); +EXPORT_SYMBOL(crisv32_io_get); /* Functions masking/unmasking interrupts */ EXPORT_SYMBOL(mask_irq); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/debugport.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/debugport.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/debugport.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/debugport.c 2006-10-13 14:43:13.000000000 +0200 @@ -4,18 +4,13 @@ #include #include -#include -#include -#include #include -#include +#include +#include #include #include #include -#include -#include - struct dbg_port { unsigned char nbr; @@ -26,7 +21,7 @@ unsigned int bits; }; -struct dbg_port ports[] = +struct dbg_port ports[] = { { 0, @@ -89,15 +84,6 @@ #endif #endif -#ifdef CONFIG_ETRAXFS_SIM -extern void print_str( const char *str ); -static char buffer[1024]; -static char msg[] = "Debug: "; -static int buffer_pos = sizeof(msg) - 1; -#endif - -extern struct tty_driver *serial_driver; - static void start_port(struct dbg_port* p) { @@ -118,7 +104,7 @@ /* Set up serial port registers */ reg_ser_rw_tr_ctrl tr_ctrl = {0}; reg_ser_rw_tr_dma_en tr_dma_en = {0}; - + reg_ser_rw_rec_ctrl rec_ctrl = {0}; reg_ser_rw_tr_baud_div tr_baud_div = {0}; reg_ser_rw_rec_baud_div rec_baud_div = {0}; @@ -148,6 +134,7 @@ tr_ctrl.data_bits = regk_ser_bits7; rec_ctrl.data_bits = regk_ser_bits7; } + REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div); REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div); @@ -156,124 +143,21 @@ REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); } -/* No debug */ -#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - return; -} - -/* Target debug */ -#elif !defined(CONFIG_ETRAXFS_SIM) - -static void -console_write_direct(struct console *co, const char *buf, unsigned int len) -{ - int i; - reg_ser_r_stat_din stat; - reg_ser_rw_tr_dma_en tr_dma_en, old; - - /* Switch to manual mode */ - tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); - if (tr_dma_en.en == regk_ser_yes) { - tr_dma_en.en = regk_ser_no; - REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); - } - - /* Send data */ - for (i = 0; i < len; i++) { - /* LF -> CRLF */ - if (buf[i] == '\n') { - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, '\r'); - } - /* Wait until transmitter is ready and send.*/ - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, buf[i]); - } - - /* Restore mode */ - if (tr_dma_en.en != old.en) - REG_WR(ser, port->instance, rw_tr_dma_en, old); -} - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - if (!port) - return; - console_write_direct(co, buf, len); -} - - - -#else - -/* VCS debug */ - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - char* pos; - pos = memchr(buf, '\n', len); - if (pos) { - int l = ++pos - buf; - memcpy(buffer + buffer_pos, buf, l); - memcpy(buffer, msg, sizeof(msg) - 1); - buffer[buffer_pos + l] = '\0'; - print_str(buffer); - buffer_pos = sizeof(msg) - 1; - if (pos - buf != len) { - memcpy(buffer + buffer_pos, pos, len - l); - buffer_pos += len - l; - } - } else { - memcpy(buffer + buffer_pos, buf, len); - buffer_pos += len; - } -} - -#endif - -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write(NULL, buf, strlen(buf)); - return printed_len; -} - -void -stupid_debug(char* buf) -{ - console_write(NULL, buf, strlen(buf)); -} - #ifdef CONFIG_ETRAX_KGDB /* Use polling to get a single character from the kernel debug port */ int getDebugChar(void) { - reg_ser_rs_status_data stat; + reg_ser_rs_stat_din stat; reg_ser_rw_ack_intr ack_intr = { 0 }; do { - stat = REG_RD(ser, kgdb_instance, rs_status_data); - } while (!stat.data_avail); + stat = REG_RD(ser, kgdb_port->instance, rs_stat_din); + } while (!stat.dav); /* Ack the data_avail interrupt. */ - ack_intr.data_avail = 1; - REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); + ack_intr.dav = 1; + REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr); return stat.data; } @@ -282,173 +166,18 @@ void putDebugChar(int val) { - reg_ser_r_status_data stat; + reg_ser_r_stat_din stat; do { - stat = REG_RD (ser, kgdb_instance, r_status_data); - } while (!stat.tr_ready); - REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); + stat = REG_RD (ser, kgdb_port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT (ser, kgdb_port->instance, rw_dout, val); } #endif /* CONFIG_ETRAX_KGDB */ -static int __init -console_setup(struct console *co, char *options) -{ - char* s; - - if (options) { - port = &ports[co->index]; - port->baudrate = 115200; - port->parity = 'N'; - port->bits = 8; - port->baudrate = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) port->parity = *s++; - if (*s) port->bits = *s++ - '0'; - port->started = 0; - start_port(port); - } - return 0; -} - -/* This is a dummy serial device that throws away anything written to it. - * This is used when no debug output is wanted. - */ -static struct tty_driver dummy_driver; - -static int dummy_open(struct tty_struct *tty, struct file * filp) -{ - return 0; -} - -static void dummy_close(struct tty_struct *tty, struct file * filp) -{ -} - -static int dummy_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - return count; -} - -static int -dummy_write_room(struct tty_struct *tty) -{ - return 8192; -} - -void __init -init_dummy_console(void) -{ - memset(&dummy_driver, 0, sizeof(struct tty_driver)); - dummy_driver.driver_name = "serial"; - dummy_driver.name = "ttyS"; - dummy_driver.major = TTY_MAJOR; - dummy_driver.minor_start = 68; - dummy_driver.num = 1; /* etrax100 has 4 serial ports */ - dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; - dummy_driver.subtype = SERIAL_TYPE_NORMAL; - dummy_driver.init_termios = tty_std_termios; - dummy_driver.init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ - dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - - dummy_driver.open = dummy_open; - dummy_driver.close = dummy_close; - dummy_driver.write = dummy_write; - dummy_driver.write_room = dummy_write_room; - if (tty_register_driver(&dummy_driver)) - panic("Couldn't register dummy serial driver\n"); -} - -static struct tty_driver* -crisv32_console_device(struct console* co, int *index) -{ - if (port) - *index = port->nbr; - return port ? serial_driver : &dummy_driver; -} - -static struct console sercons = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : -1, - cflag : 0, - next : NULL -}; -static struct console sercons0 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 0, - cflag : 0, - next : NULL -}; - -static struct console sercons1 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 1, - cflag : 0, - next : NULL -}; -static struct console sercons2 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 2, - cflag : 0, - next : NULL -}; -static struct console sercons3 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 3, - cflag : 0, - next : NULL -}; - /* Register console for printk's, etc. */ int __init init_etrax_debug(void) { - static int first = 1; - - if (!first) { - unregister_console(&sercons); - register_console(&sercons0); - register_console(&sercons1); - register_console(&sercons2); - register_console(&sercons3); - init_dummy_console(); - return 0; - } - first = 0; - register_console(&sercons); start_port(port); #ifdef CONFIG_ETRAX_KGDB @@ -456,5 +185,3 @@ #endif /* CONFIG_ETRAX_KGDB */ return 0; } - -__initcall(init_etrax_debug); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/dma.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/dma.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/dma.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/dma.c 2007-02-02 08:45:22.000000000 +0100 @@ -12,6 +12,16 @@ #include #include +/* + * The memory region we allocated bandwidth for is stored in + * used_dma_channels as an in-use flag. + */ +enum dma_region_allocated_marker { + DMA_NO_REGION_ALLOCATED = 0, + DMA_INT_REGION_ALLOCATED = 1, + DMA_EXT_REGION_ALLOCATED = 2 +}; + static char used_dma_channels[MAX_DMA_CHANNELS]; static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; @@ -74,7 +84,7 @@ if (options & DMA_VERBOSE_ON_ERROR) { printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1); } - + if (options & DMA_PANIC_ON_ERROR) panic("request_dma error!"); return -EINVAL; @@ -202,13 +212,14 @@ if (dmanr == 3) strmux_cfg.dma3 = regk_strmux_ext3; else if (dmanr == 9) - strmux_cfg.dma9 = regk_strmux_ext2; + strmux_cfg.dma9 = regk_strmux_ext3; else - panic("Invalid DMA channel for ext2\n"); + panic("Invalid DMA channel for ext3\n"); break; } - used_dma_channels[dmanr] = 1; + used_dma_channels[dmanr] = options & DMA_INT_MEM + ? DMA_INT_REGION_ALLOCATED : DMA_EXT_REGION_ALLOCATED; used_dma_channels_users[dmanr] = device_id; REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); @@ -218,7 +229,12 @@ void crisv32_free_dma(unsigned int dmanr) { + int region; + spin_lock(&dma_lock); + region = used_dma_channels[dmanr] == DMA_INT_REGION_ALLOCATED + ? INT_REGION : EXT_REGION; used_dma_channels[dmanr] = 0; + crisv32_arbiter_deallocate_bandwidth(dmanr, region); spin_unlock(&dma_lock); } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/entry.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/entry.S --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/entry.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/entry.S 2007-01-09 10:29:19.000000000 +0100 @@ -11,7 +11,7 @@ * * Stack layout in 'ret_from_system_call': * ptrace needs to have all regs on the stack. - * if the order here is changed, it needs to be + * if the order here is changed, it needs to be * updated in fork.c:copy_process, signal.c:do_signal, * ptrace.c and ptrace.h * @@ -40,7 +40,7 @@ .globl sys_call_table ; Check if preemptive kernel scheduling should be done. -#ifdef CONFIG_PREEMPT +#ifdef CONFIG_PREEMPT _resume_kernel: di ; Load current task struct. @@ -81,7 +81,7 @@ nop ba ret_from_sys_call nop - + ret_from_intr: ;; Check for resched if preemptive kernel, or if we're going back to ;; user-mode. This test matches the user_regs(regs) macro. Don't simply @@ -93,7 +93,7 @@ bpl _resume_kernel ; Note that di below is in delay slot. - + _resume_userspace: di ; So need_resched and sigpending don't change. @@ -107,19 +107,19 @@ nop ba _Rexit nop - + ;; The system_call is called by a BREAK instruction, which looks pretty ;; much like any other exception. ;; ;; System calls can't be made from interrupts but we still stack ERP ;; to have a complete stack frame. - ;; + ;; ;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12, ;; r13,mof,srp ;; ;; This function looks on the _surface_ like spaghetti programming, but it's - ;; really designed so that the fast-path does not force cache-loading of - ;; non-used instructions. Only the non-common cases cause the outlined code + ;; really designed so that the fast-path does not force cache-loading of + ;; non-used instructions. Only the non-common cases cause the outlined code ;; to run.. system_call: @@ -151,7 +151,7 @@ or.d (1<<9), $r0 move $r0, $ccs #endif - + movs.w -ENOSYS, $r0 addoq +PT_r10, $sp, $acr move.d $r0, [$acr] @@ -166,9 +166,9 @@ bmi _syscall_trace_entry nop -_syscall_traced: +_syscall_traced: ;; Check for sanity in the requested syscall number. - cmpu.w NR_syscalls, $r9 + cmpu.w NR_syscalls, $r9 bhs ret_from_sys_call lslq 2, $r9 ; Multiply by 4, in the delay slot. @@ -177,7 +177,7 @@ move.d $sp, $r0 subq 4, $sp move.d $r0, [$sp] - + ;; The registers carrying parameters (R10-R13) are intact. The optional ;; fifth and sixth parameters is in MOF and SRP respectivly. Put them ;; back on the stack. @@ -185,33 +185,33 @@ move $srp, [$sp] subq 4, $sp move $mof, [$sp] - + ;; Actually to the system call. addo.d +sys_call_table, $r9, $acr move.d [$acr], $acr jsr $acr nop - + addq 3*4, $sp ; Pop the mof, srp and regs parameters. addoq +PT_r10, $sp, $acr move.d $r10, [$acr] ; Save the return value. - moveq 1, $r9 ; "Parameter" to ret_from_sys_call to + moveq 1, $r9 ; "Parameter" to ret_from_sys_call to ; show it was a sys call. - + ;; Fall through into ret_from_sys_call to return. - + ret_from_sys_call: ;; R9 is a parameter: ;; >= 1 from syscall ;; 0 from irq - + ;; Get the current task-struct pointer. - movs.w -8192, $r0 ; THREAD_SIZE == 8192 + movs.w -8192, $r0 ; THREAD_SIZE == 8192 and.d $sp, $r0 di ; Make sure need_resched and sigpending don't change. - + addoq +TI_flags, $r0, $acr move.d [$acr], $r1 and.d _TIF_ALLWORK_MASK, $r1 @@ -253,14 +253,14 @@ move.d $r1, $r9 ba _resume_userspace nop - + _work_pending: addoq +TI_flags, $r0, $acr move.d [$acr], $r10 btstq TIF_NEED_RESCHED, $r10 ; Need resched? bpl _work_notifysig ; No, must be signal/notify. nop - + _work_resched: move.d $r9, $r1 ; Preserve R9. jsr schedule @@ -281,28 +281,26 @@ ;; Deal with pending signals and notify-resume requests. addoq +TI_flags, $r0, $acr - move.d [$acr], $r13 ; The thread_info_flags parameter. - move.d $r9, $r10 ; do_notify_resume syscall/irq param. - moveq 0, $r11 ; oldset param - 0 in this case. - move.d $sp, $r12 ; The regs param. + move.d [$acr], $r12 ; The thread_info_flags parameter. + move.d $sp, $r11 ; The regs param. jsr do_notify_resume - nop - + move.d $r9, $r10 ; do_notify_resume syscall/irq param. + ba _Rexit nop ;; We get here as a sidetrack when we've entered a syscall with the ;; trace-bit set. We need to call do_syscall_trace and then continue ;; with the call. - + _syscall_trace_entry: ;; PT_r10 in the frame contains -ENOSYS as required, at this point. - + jsr do_syscall_trace nop ;; Now re-enter the syscall code to do the syscall itself. We need to - ;; restore R9 here to contain the wanted syscall, and the other + ;; restore R9 here to contain the wanted syscall, and the other ;; parameter-bearing registers. addoq +PT_r9, $sp, $acr move.d [$acr], $r9 @@ -318,10 +316,10 @@ move [$acr], $mof addoq +PT_srp, $sp, $acr move [$acr], $srp - + ba _syscall_traced nop - + ;; Resume performs the actual task-switching, by switching stack ;; pointers. Input arguments are: ;; @@ -331,7 +329,7 @@ ;; ;; Returns old current in R10. -resume: +resume: subq 4, $sp move $srp, [$sp] ; Keep old/new PC on the stack. add.d $r12, $r10 ; R10 = current tasks tss. @@ -341,14 +339,14 @@ addoq +THREAD_usp, $r10, $acr move $usp, [$acr] ; Save user-mode stackpointer. - + ;; See copy_thread for the reason why register R9 is saved. subq 10*4, $sp movem $r9, [$sp] ; Save non-scratch registers and R9. - + addoq +THREAD_ksp, $r10, $acr move.d $sp, [$acr] ; Save kernel SP for old task. - + move.d $sp, $r10 ; Return last running task in R10. and.d -8192, $r10 ; Get thread_info from stackpointer. addoq +TI_task, $r10, $acr @@ -360,7 +358,7 @@ addoq +THREAD_usp, $r11, $acr move [$acr], $usp ; Restore user-mode stackpointer. - + addoq +THREAD_ccs, $r11, $acr move [$acr], $ccs ; Restore IRQ enable status. move.d [$sp+], $acr @@ -407,7 +405,7 @@ movem [$sp+], $r13 move.d [$sp+], $acr move [$sp], $srs - addq 4, $sp + addq 4, $sp move [$sp+], $mof move [$sp+], $spc move [$sp+], $ccs @@ -419,7 +417,7 @@ .comm cause_of_death, 4 ;; Don't declare this anywhere. -spurious_interrupt: +spurious_interrupt: di jump hard_reset_now nop @@ -494,31 +492,38 @@ ;; thread_info as first parameter move.d $r9, $r10 moveq 5, $r11 ; SIGTRAP as second argument. - jsr ugdb_trap_user + jsr ugdb_trap_user nop jump ret_from_intr ; Use the return routine for interrupts. nop - -gdb_handle_exception: + +gdb_handle_exception: subq 4, $sp move.d $r0, [$sp] #ifdef CONFIG_ETRAX_KGDB - move $ccs, $r0 ; U-flag not affected by previous insns. + move $ccs, $r0 ; U-flag not affected by previous insns. btstq 16, $r0 ; Test the U-flag. - bmi _ugdb_handle_exception ; Go to user mode debugging. - nop ; Empty delay-slot (cannot pop R0 here). + bmi _ugdb_handle_exception ; Go to user mode debugging. + nop ; Empty delay-slot (cannot pop R0 here). ba kgdb_handle_exception ; Go to kernel debugging. move.d [$sp+], $r0 ; Restore R0 in delay slot. #endif - + _ugdb_handle_exception: ba do_sigtrap ; SIGTRAP the offending process. move.d [$sp+], $r0 ; Restore R0 in delay slot. + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop + .data .section .rodata,"a" -sys_call_table: +sys_call_table: .long sys_restart_syscall ; 0 - old "setup()" system call, used ; for restarting. .long sys_exit @@ -647,7 +652,7 @@ .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask - .long sys_ni_syscall /* old "create_module" */ + .long sys_ni_syscall /* old "create_module" */ .long sys_init_module .long sys_delete_module .long sys_ni_syscall /* 130: old "get_kernel_syms" */ @@ -789,7 +794,7 @@ .long sys_clock_getres .long sys_clock_nanosleep .long sys_statfs64 - .long sys_fstatfs64 + .long sys_fstatfs64 .long sys_tgkill /* 270 */ .long sys_utimes .long sys_fadvise64_64 @@ -805,7 +810,43 @@ .long sys_mq_getsetattr .long sys_ni_syscall /* reserved for kexec */ .long sys_waitid - + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + /* * NOTE!! This doesn't have to be exact - we just have * to make sure we have _enough_ of the "sys_ni_syscall" @@ -816,4 +857,4 @@ .rept NR_syscalls - (.-sys_call_table) / 4 .long sys_ni_syscall .endr - + diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/fasttimer.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/fasttimer.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/fasttimer.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/fasttimer.c 2007-01-09 10:29:19.000000000 +0100 @@ -1,110 +1,10 @@ -/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $ +/* * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX FS * This may be useful in other OS than Linux so use 2 space indentation... * - * $Log: fasttimer.c,v $ - * Revision 1.11 2005/01/04 11:15:46 starvik - * Don't share timer IRQ. - * - * Revision 1.10 2004/12/07 09:19:38 starvik - * Corrected includes. - * Use correct interrupt macros. - * - * Revision 1.9 2004/05/14 10:18:58 starvik - * Export fast_timer_list - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.7 2003/07/10 12:06:14 starvik - * Return IRQ_NONE if irq wasn't handled - * - * Revision 1.6 2003/07/04 08:27:49 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/05 10:16:22 johana - * New INTR_VECT macros. - * - * Revision 1.4 2003/06/03 08:49:45 johana - * Fixed typo. - * - * Revision 1.3 2003/06/02 12:51:27 johana - * Now compiles. - * Commented some include files that probably can be removed. - * - * Revision 1.2 2003/06/02 12:09:41 johana - * Ported to ETRAX FS using the trig interrupt instead of timer1. - * - * Revision 1.3 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.2 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.1 2002/11/18 07:58:06 starvik - * Fast timers (from Linux 2.4) - * - * Revision 1.5 2002/10/15 06:21:39 starvik - * Added call to init_waitqueue_head - * - * Revision 1.4 2002/05/28 17:47:59 johana - * Added del_fast_timer() - * - * Revision 1.3 2002/05/28 16:16:07 johana - * Handle empty fast_timer_list - * - * Revision 1.2 2002/05/27 15:38:42 johana - * Made it compile without warnings on Linux 2.4. - * (includes, wait_queue, PROC_FS and snprintf) - * - * Revision 1.1 2002/05/27 15:32:25 johana - * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. - * - * Revision 1.8 2001/11/27 13:50:40 pkj - * Disable interrupts while stopping the timer and while modifying the - * list of active timers in timer1_handler() as it may be interrupted - * by other interrupts (e.g., the serial interrupt) which may add fast - * timers. - * - * Revision 1.7 2001/11/22 11:50:32 pkj - * * Only store information about the last 16 timers. - * * proc_fasttimer_read() now uses an allocated buffer, since it - * requires more space than just a page even for only writing the - * last 16 timers. The buffer is only allocated on request, so - * unless /proc/fasttimer is read, it is never allocated. - * * Renamed fast_timer_started to fast_timers_started to match - * fast_timers_added and fast_timers_expired. - * * Some clean-up. - * - * Revision 1.6 2000/12/13 14:02:08 johana - * Removed volatile for fast_timer_list - * - * Revision 1.5 2000/12/13 13:55:35 johana - * Added DEBUG_LOG, added som cli() and cleanup - * - * Revision 1.4 2000/12/05 13:48:50 johana - * Added range check when writing proc file, modified timer int handling - * - * Revision 1.3 2000/11/23 10:10:20 johana - * More debug/logging possibilities. - * Moved GET_JIFFIES_USEC() to timex.h and time.c - * - * Revision 1.2 2000/11/01 13:41:04 johana - * Clean up and bugfixes. - * Created new do_gettimeofday_fast() that gets a timeval struct - * with time based on jiffies and *R_TIMER0_DATA, uses a table - * for fast conversion of timer value to microseconds. - * (Much faster the standard do_gettimeofday() and we don't really - * wan't to use the true time - we wan't the "uptime" so timers don't screw up - * when we change the time. - * TODO: Add efficient support for continuous timers as well. - * - * Revision 1.1 2000/10/26 15:49:16 johana - * Added fasttimer, highresolution timers. - * - * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden */ #include @@ -128,13 +28,13 @@ #include #include -/* - * timer0 is running at 100MHz and generating jiffies timer ticks +/* + * timer0 is running at 100MHz and generating jiffies timer ticks * at 100 or 1000 HZ. * fasttimer gives an API that gives timers that expire "between" the jiffies * giving microsecond resolution (10 ns). * fasttimer uses reg_timer_rw_trig register to get interrupt when - * r_time reaches a certain value. + * r_time reaches a certain value. */ @@ -151,19 +51,19 @@ #define SANITYCHECK(x) #endif -#define D1(x) -#define D2(x) -#define DP(x) +#define D1(x) +#define D2(x) +#define DP(x) #define __INLINE__ inline -static int fast_timer_running = 0; -static int fast_timers_added = 0; -static int fast_timers_started = 0; -static int fast_timers_expired = 0; -static int fast_timers_deleted = 0; -static int fast_timer_is_init = 0; -static int fast_timer_ints = 0; +static unsigned int fast_timer_running = 0; +static unsigned int fast_timers_added = 0; +static unsigned int fast_timers_started = 0; +static unsigned int fast_timers_expired = 0; +static unsigned int fast_timers_deleted = 0; +static unsigned int fast_timer_is_init = 0; +static unsigned int fast_timer_ints = 0; struct fast_timer *fast_timer_list = NULL; @@ -171,8 +71,8 @@ #define DEBUG_LOG_MAX 128 static const char * debug_log_string[DEBUG_LOG_MAX]; static unsigned long debug_log_value[DEBUG_LOG_MAX]; -static int debug_log_cnt = 0; -static int debug_log_cnt_wrapped = 0; +static unsigned int debug_log_cnt = 0; +static unsigned int debug_log_cnt_wrapped = 0; #define DEBUG_LOG(string, value) \ { \ @@ -202,48 +102,33 @@ int timer_div_settings[NUM_TIMER_STATS]; int timer_delay_settings[NUM_TIMER_STATS]; +struct work_struct fast_work; static void -timer_trig_handler(void); +timer_trig_handler(void* dummy); /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ -void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +void __INLINE__ do_gettimeofday_fast(struct fasttime_t *tv) { - unsigned long sec = jiffies; - unsigned long usec = GET_JIFFIES_USEC(); - - usec += (sec % HZ) * (1000000 / HZ); - sec = sec / HZ; - - if (usec > 1000000) - { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_jiff = jiffies; + tv->tv_usec = GET_JIFFIES_USEC(); } -int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +int __INLINE__ timeval_cmp(struct fasttime_t *t0, struct fasttime_t *t1) { - if (t0->tv_sec < t1->tv_sec) - { + /* Compare jiffies. Takes care of wrapping */ + if (time_before(t0->tv_jiff, t1->tv_jiff)) return -1; - } - else if (t0->tv_sec > t1->tv_sec) - { + else if (time_after(t0->tv_jiff, t1->tv_jiff)) return 1; - } + + /* Compare us */ if (t0->tv_usec < t1->tv_usec) - { return -1; - } else if (t0->tv_usec > t1->tv_usec) - { return 1; - } return 0; } @@ -254,20 +139,23 @@ reg_timer_rw_intr_mask intr_mask; reg_timer_rw_trig trig; reg_timer_rw_trig_cfg trig_cfg = { 0 }; - reg_timer_r_time r_time; - - r_time = REG_RD(timer, regi_timer, r_time); + reg_timer_r_time r_time0; + reg_timer_r_time r_time1; + unsigned char trig_wrap; + unsigned char time_wrap; + r_time0 = REG_RD(timer, regi_timer, r_time); + D1(printk("start_timer_trig : %d us freq: %i div: %i\n", delay_us, freq_index, div)); /* Clear trig irq */ intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); intr_mask.trig = 0; REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); - - /* Set timer values */ + + /* Set timer values and check if trigger wraps. */ /* r_time is 100MHz (10 ns resolution) */ - trig = r_time + delay_us*(1000/10); + trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0; timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig; timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; @@ -275,15 +163,17 @@ /* Ack interrupt */ ack_intr.trig = 1; REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); - + /* Start timer */ REG_WR(timer, regi_timer, rw_trig, trig); trig_cfg.tmr = regk_timer_time; REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); /* Check if we have already passed the trig time */ - r_time = REG_RD(timer, regi_timer, r_time); - if (r_time < trig) { + r_time1 = REG_RD(timer, regi_timer, r_time); + time_wrap = r_time1 < r_time0; + + if ((trig_wrap && !time_wrap) || (r_time1 < trig)) { /* No, Enable trig irq */ intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); intr_mask.trig = 1; @@ -291,16 +181,17 @@ fast_timers_started++; fast_timer_running = 1; } - else + else { /* We have passed the time, disable trig point, ack intr */ trig_cfg.tmr = regk_timer_off; REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); - /* call the int routine directly */ - timer_trig_handler(); + /* call the int routine */ + INIT_WORK(&fast_work, timer_trig_handler, (void*)NULL); + schedule_work(&fast_work); } - + } /* In version 1.4 this function takes 27 - 50 us */ @@ -327,7 +218,7 @@ { printk("timer name: %s data: 0x%08lX already in list!\n", name, data); sanity_failed++; - return; + goto done; } else { @@ -343,11 +234,11 @@ t->name = name; t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; - t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; + t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ; if (t->tv_expires.tv_usec > 1000000) { t->tv_expires.tv_usec -= 1000000; - t->tv_expires.tv_sec++; + t->tv_expires.tv_jiff += HZ; } #ifdef FAST_TIMER_LOG timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; @@ -388,6 +279,7 @@ D2(printk("start_one_shot_timer: %d us done\n", delay_us)); +done: local_irq_restore(flags); } /* start_one_shot_timer */ @@ -431,26 +323,32 @@ /* Timer interrupt handler for trig interrupts */ static irqreturn_t -timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_trig_interrupt(int irq, void *dev_id) { reg_timer_r_masked_intr masked_intr; - /* Check if the timer interrupt is for us (a trig int) */ masked_intr = REG_RD(timer, regi_timer, r_masked_intr); if (!masked_intr.trig) return IRQ_NONE; - timer_trig_handler(); + timer_trig_handler(NULL); return IRQ_HANDLED; } -static void timer_trig_handler(void) +static void timer_trig_handler(void* dummy) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; reg_timer_rw_trig_cfg trig_cfg = { 0 }; struct fast_timer *t; - unsigned long flags; + unsigned long flags; + /* We keep interrupts disabled not only when we modify the + * fast timer list, but any time we hold a reference to a + * timer in the list, since del_fast_timer may be called + * from (another) interrupt context. Thus, the only time + * when interrupts are enabled is when calling the timer + * callback function. + */ local_irq_save(flags); /* Clear timer trig interrupt */ @@ -470,16 +368,17 @@ fast_timer_running = 0; fast_timer_ints++; - local_irq_restore(flags); + fast_timer_function_type *f; + unsigned long d; t = fast_timer_list; while (t) { - struct timeval tv; + struct fasttime_t tv; /* Has it really expired? */ do_gettimeofday_fast(&tv); - D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + D1(printk("t: %is %06ius\n", tv.tv_jiff, tv.tv_usec)); if (timeval_cmp(&t->tv_expires, &tv) <= 0) { @@ -490,7 +389,6 @@ fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - local_irq_save(flags); if (t->prev) { t->prev->next = t->next; @@ -505,11 +403,21 @@ } t->prev = NULL; t->next = NULL; - local_irq_restore(flags); - if (t->function != NULL) + /* Save function callback data before enabling interrupts, + * since the timer may be removed and we don't know how it + * was allocated (e.g. ->function and ->data may become + * overwritten after deletion if the timer was stack-allocated). + */ + f = t->function; + d = t->data; + + if (f != NULL) { - t->function(t->data); + /* Run the callback function with interrupts enabled. */ + local_irq_restore(flags); + f(d); + local_irq_save(flags); } else { @@ -522,16 +430,19 @@ D1(printk(".\n")); } - local_irq_save(flags); if ((t = fast_timer_list) != NULL) { /* Start next timer.. */ - long us; - struct timeval tv; + long us = 0; + struct fasttime_t tv; do_gettimeofday_fast(&tv); - us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + - t->tv_expires.tv_usec - tv.tv_usec); + + /* time_after_eq takes care of wrapping */ + if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff)) + us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * 1000000 / HZ + + t->tv_expires.tv_usec - tv.tv_usec); + if (us > 0) { if (!fast_timer_running) @@ -541,7 +452,6 @@ #endif start_timer_trig(us); } - local_irq_restore(flags); break; } else @@ -552,9 +462,10 @@ D1(printk("e! %d\n", us)); } } - local_irq_restore(flags); } + local_irq_restore(flags); + if (!t) { D1(printk("ttrig stop!\n")); @@ -577,28 +488,17 @@ void schedule_usleep(unsigned long us) { struct fast_timer t; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t sleep_wait; init_waitqueue_head(&sleep_wait); - { - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue *sleep_wait = NULL; - struct wait_queue wait = { current, NULL }; -#endif D1(printk("schedule_usleep(%d)\n", us)); - add_wait_queue(&sleep_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, "usleep"); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&sleep_wait, &wait); + /* Uninterruptible sleep on the fast timer. (The condition is somewhat + redundant since the timer is what wakes us up.) */ + wait_event(sleep_wait, !fast_timer_pending(&t)); + D1(printk("done schedule_usleep(%d)\n", us)); -#ifdef DECLARE_WAITQUEUE - } -#endif } #ifdef CONFIG_PROC_FS @@ -638,7 +538,7 @@ unsigned long flags; int i = 0; int num_to_show; - struct timeval tv; + struct fasttime_t tv; struct fast_timer *t, *nextt; static char *bigbuf = NULL; static unsigned long used; @@ -646,7 +546,8 @@ if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) { used = 0; - bigbuf[0] = '\0'; + if (buf) + buf[0] = '\0'; return 0; } @@ -668,7 +569,7 @@ used += sprintf(bigbuf + used, "Fast timer running: %s\n", fast_timer_running ? "yes" : "no"); used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", - (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_jiff, (unsigned long)tv.tv_usec); #ifdef FAST_TIMER_SANITY_CHECKS used += sprintf(bigbuf + used, "Sanity failed: %i\n", @@ -717,9 +618,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -739,9 +640,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -759,9 +660,9 @@ "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -772,7 +673,6 @@ used += sprintf(bigbuf + used, "Active timers:\n"); local_irq_save(flags); - local_irq_save(flags); t = fast_timer_list; while (t != NULL && (used+100 < BIG_BUF_SIZE)) { @@ -783,15 +683,15 @@ /* " func: 0x%08lX" */ "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data /* , t->function */ ); - local_irq_disable(); + local_irq_save(flags); if (t->next != nextt) { printk("timer removed!\n"); @@ -822,7 +722,7 @@ static struct fast_timer tr[10]; static int exp_num[10]; -static struct timeval tv_exp[100]; +static struct fasttime_t tv_exp[100]; static void test_timeout(unsigned long data) { @@ -860,7 +760,7 @@ int prev_num; int j; - struct timeval tv, tv0, tv1, tv2; + struct fasttime_t tv, tv0, tv1, tv2; printk("fast_timer_test() start\n"); do_gettimeofday_fast(&tv); @@ -873,7 +773,7 @@ { do_gettimeofday_fast(&tv_exp[j]); } - printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + printk("fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec); for (j = 0; j < 1000; j++) { @@ -883,11 +783,11 @@ for (j = 0; j < 100; j++) { printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", - tv_exp[j].tv_sec,tv_exp[j].tv_usec, - tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, - tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, - tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, - tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + tv_exp[j].tv_jiff,tv_exp[j].tv_usec, + tv_exp[j+1].tv_jiff,tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_jiff,tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_jiff,tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_jiff,tv_exp[j+4].tv_usec); j += 4; } do_gettimeofday_fast(&tv0); @@ -919,9 +819,9 @@ } } do_gettimeofday_fast(&tv2); - printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); - printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); - printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + printk("Timers started %is %06i\n", tv0.tv_jiff, tv0.tv_usec); + printk("Timers started at %is %06i\n", tv1.tv_jiff, tv1.tv_usec); + printk("Timers done %is %06i\n", tv2.tv_jiff, tv2.tv_usec); DP(printk("buf0:\n"); printk(buf0); printk("buf1:\n"); @@ -943,9 +843,9 @@ printk("%-10s set: %6is %06ius exp: %6is %06ius " "data: 0x%08X func: 0x%08X\n", t->name, - t->tv_set.tv_sec, + t->tv_set.tv_jiff, t->tv_set.tv_usec, - t->tv_expires.tv_sec, + t->tv_expires.tv_jiff, t->tv_expires.tv_usec, t->data, t->function @@ -953,10 +853,10 @@ printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", t->delay_us, - tv_exp[j].tv_sec, + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, exp_num[j], - (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); } proc_fasttimer_read(buf5, NULL, 0, 0, 0); printk("buf5 after all done:\n"); @@ -966,7 +866,7 @@ #endif -void fast_timer_init(void) +int fast_timer_init(void) { /* For some reason, request_irq() hangs when called froom time_init() */ if (!fast_timer_is_init) @@ -981,10 +881,10 @@ proc_register_dynamic(&proc_root, &fasttimer_proc_entry); #endif #endif /* PROC_FS */ - if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, IRQF_DISABLED, - "fast timer int", NULL)) + if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, SA_SHIRQ | SA_INTERRUPT, + "fast timer int", &fast_timer_list)) { - printk("err: timer1 irq\n"); + printk("err: fasttimer irq\n"); } fast_timer_is_init = 1; #ifdef FAST_TIMER_TEST @@ -992,4 +892,6 @@ fast_timer_test(); #endif } + return 0; } +__initcall(fast_timer_init); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/head.S --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/head.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/head.S 2007-01-09 10:29:19.000000000 +0100 @@ -4,7 +4,6 @@ * Copyright (C) 2003, Axis Communications AB */ - #define ASSEMBLER_MACROS_ONLY /* @@ -12,14 +11,21 @@ * -traditional must not be used when assembling this file. */ #include +#include +#include #include #include #include #include - +#include +#include + #define CRAMFS_MAGIC 0x28cd3d45 +#define JHEAD_MAGIC 0x1FF528A6 +#define JHEAD_SIZE 8 #define RAM_INIT_MAGIC 0x56902387 -#define COMMAND_LINE_MAGIC 0x87109563 +#define COMMAND_LINE_MAGIC 0x87109563 +#define NAND_BOOT_MAGIC 0x9a9db001 ;; NOTE: R8 and R9 carry information from the decompressor (if the ;; kernel was compressed). They must not be used in the code below @@ -30,11 +36,10 @@ .global romfs_start .global romfs_length .global romfs_in_flash + .global nand_boot .global swapper_pg_dir - .global crisv32_nand_boot - .global crisv32_nand_cramfs_offset - ;; Dummy section to make it bootable with current VCS simulator + ;; Dummy section to make it bootable with current VCS simulator #ifdef CONFIG_ETRAXFS_SIM .section ".boot", "ax" ba tstart @@ -42,13 +47,13 @@ #endif .text -tstart: +tstart: ;; This is the entry point of the kernel. The CPU is currently in ;; supervisor mode. - ;; + ;; ;; 0x00000000 if flash. ;; 0x40004000 if DRAM. - ;; + ;; di ;; Start clocks for used blocks. @@ -72,20 +77,25 @@ move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0 move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1 move.d $r1, [$r0] - -#ifdef CONFIG_ETRAXFS_SIM + +#ifdef CONFIG_ETRAXFS_SIM ;; Set up minimal flash waitstates move.d 0, $r10 move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11 move.d $r10, [$r11] -#endif +#endif +#ifdef CONFIG_SMP +secondary_cpu_entry: /* Entry point for secondary CPUs */ + di +#endif + ;; Setup and enable the MMU. Use same configuration for both the data ;; and the instruction MMU. ;; ;; Note; 3 cycles is needed for a bank-select to take effect. Further; ;; bank 1 is the instruction MMU, bank 2 is the data MMU. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAXFS_SIM move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0 @@ -96,7 +106,7 @@ | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0 -#endif +#endif ;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00. move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \ @@ -146,8 +156,8 @@ | REG_STATE(mmu, rw_mm_cfg, seg_2, page) \ | REG_STATE(mmu, rw_mm_cfg, seg_1, page) \ | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2 -#endif - +#endif + ;; Update instruction MMU. move 1, $srs nop @@ -165,7 +175,7 @@ move $r0, $s2 ; kbase_hi. move $r1, $s1 ; kbase_lo move $r2, $s0 ; mm_cfg, virtual memory configuration. - + ;; Enable data and instruction MMU. move 0, $srs moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on @@ -183,17 +193,11 @@ nop nop nop - move $s10, $r0 + move $s12, $r0 cmpq 0, $r0 beq master_cpu nop slave_cpu: - ; A slave waits for cpu_now_booting to be equal to CPU ID. - move.d cpu_now_booting, $r1 -slave_wait: - cmp.d [$r1], $r0 - bne slave_wait - nop ; Time to boot-up. Get stack location provided by master CPU. move.d smp_init_current_idle_thread, $r1 move.d [$r1], $sp @@ -203,9 +207,16 @@ jsr smp_callin nop master_cpu: -#endif + /* Set up entry point for secondary CPUs. The boot ROM has set up + * EBP at start of internal memory. The CPU will get there + * later when we issue an IPI to them... */ + move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0 + move.d secondary_cpu_entry, $r1 + move.d $r1, [$r0] +#endif #ifndef CONFIG_ETRAXFS_SIM - ;; Check if starting from DRAM or flash. + ; Check if starting from DRAM (network->RAM boot or unpacked + ; compressed kernel), or directly from flash. lapcq ., $r0 and.d 0x7fffffff, $r0 ; Mask off the non-cache bit. cmp.d 0x10000, $r0 ; Arbitrary, something above this code. @@ -238,6 +249,7 @@ ;; Copy the text and data section to DRAM. This depends on that the ;; variables used below are correctly set up by the linker script. ;; The calculated value stored in R4 is used below. + ;; Leave the cramfs file system (piggybacked after the kernel) in flash. moveq 0, $r0 ; Source. move.d text_start, $r1 ; Destination. move.d __vmlinux_end, $r2 @@ -249,7 +261,7 @@ blo 1b nop - ;; Keep CRAMFS in flash. + ;; Check for cramfs. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] @@ -257,7 +269,8 @@ cmp.d CRAMFS_MAGIC, $r0 bne 1f nop - + + ;; Set length and start of cramfs, set romfs_in_flash flag addoq +4, $r4, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -273,35 +286,32 @@ nop _inram: - ;; Check if booting from NAND flash (in that case we just remember the offset - ;; into the flash where cramfs should be). - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne move_cramfs - moveq 1,$r0 - move.d crisv32_nand_boot, $r1 - move.d $r0, [$r1] - move.d crisv32_nand_cramfs_offset, $r1 - move.d $r9, [$r1] + ;; Check if booting from NAND flash; if so, set appropriate flags + ;; and move on. + cmp.d NAND_BOOT_MAGIC, $r12 + bne move_cramfs ; not nand, jump moveq 1, $r0 - move.d romfs_in_flash, $r1 + move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND + move.d $r0, [$r1] + moveq 0, $r0 ; tell axisflashmap romfs is not in + move.d romfs_in_flash, $r1 ; (directly accessed) flash move.d $r0, [$r1] - jump _start_it + jump _start_it ; continue with boot nop -move_cramfs: - ;; Move the cramfs after BSS. +move_cramfs: + ;; kernel is in DRAM. + ;; Must figure out if there is a piggybacked rootfs image or not. + ;; Set romfs_length to 0 => no rootfs image available by default. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAXFS_SIM ;; The kernel could have been unpacked to DRAM by the loader, but - ;; the cramfs image could still be inte the flash immediately - ;; following the compressed kernel image. The loaded passes the address - ;; of the bute succeeding the last compressed byte in the flash in + ;; the cramfs image could still be in the flash immediately + ;; following the compressed kernel image. The loader passes the address + ;; of the byte succeeding the last compressed byte in the flash in ;; register R9 when starting the kernel. cmp.d 0x0ffffff8, $r9 bhs _no_romfs_in_flash ; R9 points outside the flash area. @@ -309,12 +319,14 @@ #else ba _no_romfs_in_flash nop -#endif +#endif + ;; cramfs rootfs might to be in flash. Check for it. move.d [$r9], $r0 ; cramfs_super.magic cmp.d CRAMFS_MAGIC, $r0 bne _no_romfs_in_flash nop + ;; found cramfs in flash. set address and size, and romfs_in_flash flag. addoq +4, $r9, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -330,29 +342,45 @@ nop _no_romfs_in_flash: - ;; Look for cramfs. -#ifndef CONFIG_ETRAXFS_SIM + ;; No romfs in flash, so look for cramfs, or jffs2 with jhead, + ;; after kernel in RAM, as is the case with network->RAM boot. + ;; For cramfs, partition starts with magic and length. + ;; For jffs2, a jhead is prepended which contains with magic and length. + ;; The jhead is not part of the jffs2 partition however. +#ifndef CONFIG_ETRAXFS_SIM move.d __vmlinux_end, $r0 #else - move.d __end, $r0 -#endif + move.d __end, $r0 +#endif move.d [$r0], $r1 - cmp.d CRAMFS_MAGIC, $r1 - bne 2f + cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic? + beq 2f ; yes, jump + nop + cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic? + bne 4f ; no, skip copy + nop + addq 4, $r0 ; location of jffs2 size + move.d [$r0+], $r2 ; fetch jffs2 size -> r2 + ; r0 now points to start of jffs2 + ba 3f nop +2: + addoq +4, $r0, $acr ; location of cramfs size + move.d [$acr], $r2 ; fetch cramfs size -> r2 + ; r0 still points to start of cramfs +3: + ;; Now, move the root fs to after kernel's BSS - addoq +4, $r0, $acr - move.d [$acr], $r2 - move.d _end, $r1 + move.d _end, $r1 ; start of cramfs -> r1 move.d romfs_start, $r3 - move.d $r1, [$r3] + move.d $r1, [$r3] ; store at romfs_start (for axisflashmap) move.d romfs_length, $r3 - move.d $r2, [$r3] + move.d $r2, [$r3] ; store size at romfs_length -#ifndef CONFIG_ETRAXFS_SIM - add.d $r2, $r0 +#ifndef CONFIG_ETRAXFS_SIM + add.d $r2, $r0 ; copy from end and downwards add.d $r2, $r1 - + lsrq 1, $r2 ; Size is in bytes, we copy words. addq 1, $r2 1: @@ -364,17 +392,24 @@ bne 1b nop #endif - -2: + +4: + ;; BSS move done. + ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM + ;; Also clear nand_boot flag; if we got here, we know we've not + ;; booted from NAND flash. moveq 0, $r0 move.d romfs_in_flash, $r1 move.d $r0, [$r1] + moveq 0, $r0 + move.d nand_boot, $r1 + move.d $r0, [$r1] jump _start_it ; Jump to cached code. nop - + _start_it: - + ;; Check if kernel command line is supplied cmp.d COMMAND_LINE_MAGIC, $r10 bne no_command_line @@ -383,9 +418,9 @@ move.d 256, $r13 move.d cris_command_line, $r10 or.d 0x80000000, $r11 ; Make it virtual -1: - move.b [$r11+], $r12 - move.b $r12, [$r10+] +1: + move.b [$r11+], $r1 + move.b $r1, [$r10+] subq 1, $r13 bne 1b nop @@ -401,7 +436,7 @@ move.d etrax_irv, $r1 ; Set the exception base register and pointer. move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAXFS_SIM ;; Clear the BSS region from _bss_start to _end. move.d __bss_start, $r0 move.d _end, $r1 @@ -429,17 +464,31 @@ .data etrax_irv: .dword 0 + +; Variables for communication with the Axis flash map driver (axisflashmap), +; and for setting up memory in arch/cris/kernel/setup.c . + +; romfs_start is set to the start of the root file system, if it exists +; in directly accessible memory (i.e. NOR Flash when booting from Flash, +; or RAM when booting directly from a network-downloaded RAM image) romfs_start: .dword 0 + +; romfs_length is set to the size of the root file system image, if it exists +; in directly accessible memory (see romfs_start). Otherwise it is set to 0. romfs_length: .dword 0 + +; romfs_in_flash is set to 1 if the root file system resides in directly +; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot +; or NAND flash boot. romfs_in_flash: .dword 0 -crisv32_nand_boot: - .dword 0 -crisv32_nand_cramfs_offset: - .dword 0 +; nand_boot is set to 1 when the kernel has been booted from NAND flash +nand_boot: + .dword 0 + swapper_pg_dir = 0xc0002000 .section ".init.data", "aw" diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/io.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/io.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/io.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/io.c 2006-11-21 00:04:55.000000000 +0100 @@ -1,7 +1,7 @@ -/* +/* * Helper functions for I/O pins. * - * Copyright (c) 2004 Axis Communications AB. + * Copyright (c) 2004, 2006 Axis Communications AB. */ #include @@ -15,6 +15,10 @@ #include #include +#ifndef DEBUG +#define DEBUG(x) +#endif + struct crisv32_ioport crisv32_ioports[] = { { @@ -46,13 +50,15 @@ (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout), (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din), 18 - } + } }; #define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) -struct crisv32_iopin crisv32_led1_green; -struct crisv32_iopin crisv32_led1_red; +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led_net1_green; +struct crisv32_iopin crisv32_led_net1_red; struct crisv32_iopin crisv32_led2_green; struct crisv32_iopin crisv32_led2_red; struct crisv32_iopin crisv32_led3_green; @@ -76,34 +82,54 @@ static int __init crisv32_io_init(void) { int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init (&crisv32_ioports[i].lock); + spin_lock_init (&dummy_port.lock); + /* Initialize LEDs */ - ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G); - ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R); +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += crisv32_io_get_name(&crisv32_led_net0_green, CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += crisv32_io_get_name(&crisv32_led_net0_red, CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + +#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO + ret += crisv32_io_get_name(&crisv32_led_net1_green, CONFIG_ETRAX_LED_G_NET1); + crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) { + crisv32_io_get_name(&crisv32_led_net1_red, CONFIG_ETRAX_LED_R_NET1); + crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out); + } else + crisv32_led_net1_red = dummy_led; +#endif + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G); ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R); ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G); ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R); - crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); - if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R)) - crisv32_led1_red = dummy_led; - if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R)) - crisv32_led2_red = dummy_led; - return ret; } __initcall(crisv32_io_init); -int crisv32_io_get(struct crisv32_iopin* iopin, +int crisv32_io_get(struct crisv32_iopin* iopin, unsigned int port, unsigned int pin) { - if (port > NBR_OF_PORTS) + if (port > NBR_OF_PORTS) return -EINVAL; if (port > crisv32_ioports[port].pin_count) return -EINVAL; @@ -111,14 +137,17 @@ iopin->bit = 1 << pin; iopin->port = &crisv32_ioports[port]; - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port-1, pin, pin, pinmux_gpio)) return -EIO; - + DEBUG(printk("crisv32_io_get: Allocated pin %d on port %d\n", pin, port )); + return 0; } int crisv32_io_get_name(struct crisv32_iopin* iopin, - char* name) + const char* name) { int port; int pin; @@ -128,7 +157,7 @@ if (toupper(*name) < 'A' || toupper(*name) > 'E') return -EINVAL; - + port = toupper(*name) - 'A'; name++; pin = simple_strtoul(name, NULL, 10); @@ -139,9 +168,12 @@ iopin->bit = 1 << pin; iopin->port = &crisv32_ioports[port]; - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port-1, pin, pin, pinmux_gpio)) return -EIO; + DEBUG(printk("crisv32_io_get_name: Allocated pin %d on port %d\n", pin, port)); return 0; } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/irq.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/irq.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/irq.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/irq.c 2006-10-13 14:43:13.000000000 +0200 @@ -44,10 +44,10 @@ cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ }; -struct cris_irq_allocation irq_allocations[NR_IRQS] = +struct cris_irq_allocation irq_allocations[NR_IRQS] = {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; -static unsigned long irq_regs[NR_CPUS] = +static unsigned long irq_regs[NR_CPUS] = { regi_irq, #ifdef CONFIG_SMP @@ -79,9 +79,9 @@ extern void kgdb_init(void); extern void breakpoint(void); -/* - * Build the IRQ handler stubs using macros from irq.h. First argument is the - * IRQ number, the second argument is the corresponding bit in +/* + * Build the IRQ handler stubs using macros from irq.h. First argument is the + * IRQ number, the second argument is the corresponding bit in * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. */ BUILD_IRQ(0x31, (1 << 0)) /* memarb */ @@ -139,7 +139,7 @@ spin_lock_irqsave(&irq_lock, flags); intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - + /* Remember; 1 let thru, 0 block. */ intr_mask &= ~(1 << (irq - FIRST_IRQ)); @@ -152,10 +152,10 @@ { int intr_mask; unsigned long flags; - + spin_lock_irqsave(&irq_lock, flags); intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - + /* Remember; 1 let thru, 0 block. */ intr_mask |= (1 << (irq - FIRST_IRQ)); @@ -168,7 +168,7 @@ { int cpu; unsigned long flags; - + spin_lock_irqsave(&irq_lock, flags); cpu = irq_allocations[irq - FIRST_IRQ].cpu; @@ -178,12 +178,12 @@ spin_unlock_irqrestore(&irq_lock, flags); return smp_processor_id(); } - + /* Let the interrupt stay if possible */ if (cpu_isset(cpu, irq_allocations[irq - FIRST_IRQ].mask)) goto out; - + /* IRQ must be moved to another CPU. */ cpu = first_cpu(irq_allocations[irq - FIRST_IRQ].mask); irq_allocations[irq - FIRST_IRQ].cpu = cpu; @@ -287,7 +287,7 @@ * interrupt from the CPU and software has to sort out which * interrupts that happened. There are two special cases here: * - * 1. Timer interrupts may never be blocked because of the + * 1. Timer interrupts may never be blocked because of the * watchdog (refer to comment in include/asr/arch/irq.h) * 2. GDB serial port IRQs are unhandled here and will be handled * as a single IRQ when it strikes again because the GDB @@ -304,33 +304,33 @@ cpu = smp_processor_id(); /* An extra irq_enter here to prevent softIRQs to run after - * each do_IRQ. This will decrease the interrupt latency. + * each do_IRQ. This will decrease the interrupt latency. */ irq_enter(); /* Get which IRQs that happend. */ masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); - + /* Calculate new IRQ mask with these IRQs disabled. */ mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); mask &= ~masked; /* Timer IRQ is never masked */ if (masked & TIMER_MASK) - mask |= TIMER_MASK; + mask |= TIMER_MASK; /* Block all the IRQs */ REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); - + /* Check for timer IRQ and handle it special. */ if (masked & TIMER_MASK) { masked &= ~TIMER_MASK; - do_IRQ(TIMER_INTR_VECT, regs); + do_IRQ(TIMER_INTR_VECT, regs); } #ifdef IGNORE_MASK /* Remove IRQs that can't be handled as multiple. */ - masked &= ~IGNORE_MASK; + masked &= ~IGNORE_MASK; #endif /* Handle the rest of the IRQs. */ @@ -377,7 +377,7 @@ irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; - + set_exception_vector(0x00, nmi_interrupt); set_exception_vector(0x30, multiple_interrupt); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb.c 2005-07-06 11:40:49.000000000 +0200 @@ -25,7 +25,7 @@ * kgdb usage notes: * ----------------- * - * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be + * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be * built with different gcc flags: "-g" is added to get debug infos, and * "-fomit-frame-pointer" is omitted to make debugging easier. Since the * resulting kernel will be quite big (approx. > 7 MB), it will be stripped @@ -118,7 +118,7 @@ * call to kgdb_init() is necessary in order to allow any breakpoints * or error conditions to be properly intercepted and reported to gdb. * Two, a breakpoint needs to be generated to begin communication. This - * is most easily accomplished by a call to breakpoint(). + * is most easily accomplished by a call to breakpoint(). * * The following gdb commands are supported: * @@ -382,8 +382,8 @@ int getDebugChar(void); #ifdef CONFIG_ETRAXFS_SIM -int getDebugChar(void) -{ +int getDebugChar(void) +{ return socketread(); } #endif @@ -490,7 +490,7 @@ /********************************** Breakpoint *******************************/ /* Use an internal stack in the breakpoint and interrupt response routines. - FIXME: How do we know the size of this stack is enough? + FIXME: How do we know the size of this stack is enough? Global so it can be reached from assembler code. */ #define INTERNAL_STACK_SIZE 1024 char internal_stack[INTERNAL_STACK_SIZE]; @@ -511,7 +511,7 @@ gdb_cris_strcpy(char *s1, const char *s2) { char *s = s1; - + for (s = s1; (*s++ = *s2++) != '\0'; ) ; return s1; @@ -522,7 +522,7 @@ gdb_cris_strlen(const char *s) { const char *sc; - + for (sc = s; *sc != '\0'; sc++) ; return (sc - s); @@ -534,7 +534,7 @@ { const unsigned char uc = c; const unsigned char *su; - + for (su = s; 0 < n; ++su, --n) if (*su == uc) return (void *)su; @@ -549,15 +549,15 @@ char *s1; char *sd; int x = 0; - + for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1) x = x * base + (sd - hexchars); - + if (endptr) { /* Unconverted suffix is stored in endptr unless endptr is NULL. */ *endptr = s1; } - + return x; } @@ -629,7 +629,7 @@ } else if (regno == PID) { /* 32-bit register. */ *valptr = *(unsigned int *)((char *)®.pid); - + } else if (regno == SRS) { /* 8-bit register. */ *valptr = (unsigned int)(*(unsigned char *)((char *)®.srs)); @@ -726,7 +726,7 @@ *buf++ = highhex (ch); *buf++ = lowhex (ch); } - + /* Terminate properly. */ *buf = '\0'; return buf; @@ -804,7 +804,7 @@ continue; buffer[count] = 0; - + if (ch == '#') { xmitcsum = hex(getDebugChar()) << 4; xmitcsum += hex(getDebugChar()); @@ -836,7 +836,7 @@ int checksum; int runlen; int encode; - + do { char *src = buffer; putDebugChar('$'); @@ -905,42 +905,42 @@ { char *ptr = output_buffer; unsigned int reg_cont; - + /* Send trap type (converted to signal) */ - *ptr++ = 'T'; + *ptr++ = 'T'; *ptr++ = highhex(sigval); *ptr++ = lowhex(sigval); if (((reg.exs & 0xff00) >> 8) == 0xc) { - + /* Some kind of hardware watchpoint triggered. Find which one and determine its type (read/write/access). */ int S, bp, trig_bits = 0, rw_bits = 0; int trig_mask = 0; unsigned int *bp_d_regs = &sreg.s3_3; /* In a lot of cases, the stopped data address will simply be EDA. - In some cases, we adjust it to match the watched data range. + In some cases, we adjust it to match the watched data range. (We don't want to change the actual EDA though). */ unsigned int stopped_data_address; /* The S field of EXS. */ S = (reg.exs & 0xffff0000) >> 16; - + if (S & 1) { /* Instruction watchpoint. */ /* FIXME: Check against, and possibly adjust reported EDA. */ } else { /* Data watchpoint. Find the one that triggered. */ for (bp = 0; bp < 6; bp++) { - + /* Dx_RD, Dx_WR in the S field of EXS for this BP. */ int bitpos_trig = 1 + bp * 2; /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */ int bitpos_config = 2 + bp * 4; - + /* Get read/write trig bits for this BP. */ trig_bits = (S & (3 << bitpos_trig)) >> bitpos_trig; - + /* Read/write config bits for this BP. */ rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config; if (trig_bits) { @@ -949,11 +949,11 @@ if ((rw_bits == 0x1 && trig_bits != 0x1) || (rw_bits == 0x2 && trig_bits != 0x2)) panic("Invalid r/w trigging for this BP"); - + /* Mark this BP as trigged for future reference. */ trig_mask |= (1 << bp); - - if (reg.eda >= bp_d_regs[bp * 2] && + + if (reg.eda >= bp_d_regs[bp * 2] && reg.eda <= bp_d_regs[bp * 2 + 1]) { /* EDA withing range for this BP; it must be the one we're looking for. */ @@ -972,7 +972,7 @@ /* Read/write config bits for this BP (needed later). */ rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config; - + if (trig_mask & (1 << bp)) { /* EDA within 31 bytes of the configured start address? */ if (reg.eda + 31 >= bp_d_regs[bp * 2]) { @@ -987,12 +987,12 @@ } } } - + /* No match yet? */ BUG_ON(bp >= 6); /* Note that we report the type according to what the BP is configured for (otherwise we'd never report an 'awatch'), not according to how - it trigged. We did check that the trigged bits match what the BP is + it trigged. We did check that the trigged bits match what the BP is configured for though. */ if (rw_bits == 0x1) { /* read */ @@ -1110,12 +1110,12 @@ if (sigval == SIGTRAP) { /* Break 8, single step or hardware breakpoint exception. */ - + /* Check IDX field of EXS. */ if (((reg.exs & 0xff00) >> 8) == 0x18) { /* Break 8. */ - + /* Static (compiled) breakpoints must return to the next instruction in order to avoid infinite loops (default value of ERP). Dynamic (gdb-invoked) must subtract the size of the break instruction from @@ -1132,7 +1132,7 @@ reg.pc -= 2; } } - + } else if (((reg.exs & 0xff00) >> 8) == 0x3) { /* Single step. */ /* Don't fiddle with S1. */ @@ -1190,10 +1190,10 @@ unsigned int *bp_d_regs = &sreg.s3_3; /* The watchpoint allocation scheme is the simplest possible. - For example, if a region is watched for read and + For example, if a region is watched for read and a write watch is requested, a new watchpoint will be used. Also, if a watch for a region that is already - covered by one or more existing watchpoints, a new + covered by one or more existing watchpoints, a new watchpoint will be used. */ /* First, find a free data watchpoint. */ @@ -1205,13 +1205,13 @@ break; } } - + if (bp > 5) { /* We're out of watchpoints. */ gdb_cris_strcpy(output_buffer, error_message[E04]); return; } - + /* Configure the control register first. */ if (type == '3' || type == '4') { /* Trigger on read. */ @@ -1221,11 +1221,11 @@ /* Trigger on write. */ sreg.s0_3 |= (2 << (2 + bp * 4)); } - + /* Ugly pointer arithmetics to configure the watched range. */ bp_d_regs[bp * 2] = addr; bp_d_regs[bp * 2 + 1] = (addr + len - 1); - } + } /* Set the S1 flag to enable watchpoints. */ reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); @@ -1258,7 +1258,7 @@ /* Not in use. */ gdb_cris_strcpy(output_buffer, error_message[E04]); return; - } + } /* Deconfigure. */ sreg.s1_3 = 0; sreg.s2_3 = 0; @@ -1268,8 +1268,8 @@ unsigned int *bp_d_regs = &sreg.s3_3; /* Try to find a watchpoint that is configured for the specified range, then check that read/write also matches. */ - - /* Ugly pointer arithmetic, since I cannot rely on a + + /* Ugly pointer arithmetic, since I cannot rely on a single switch (addr) as there may be several watchpoints with the same start address for example. */ @@ -1279,7 +1279,7 @@ /* Matching range. */ int bitpos = 2 + bp * 4; int rw_bits; - + /* Read/write bits for this BP. */ rw_bits = (sreg.s0_3 & (0x3 << bitpos)) >> bitpos; @@ -1347,7 +1347,7 @@ (char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)), 16 * sizeof(unsigned int)); break; - } + } case 'G': /* Write registers. GXX..XX Each byte of register data is described by two hex digits. @@ -1357,11 +1357,11 @@ hex2mem((char *)®, &input_buffer[1], sizeof(registers)); /* Support registers. */ hex2mem((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)), - &input_buffer[1] + sizeof(registers), + &input_buffer[1] + sizeof(registers), 16 * sizeof(unsigned int)); gdb_cris_strcpy(output_buffer, "OK"); break; - + case 'P': /* Write register. Pn...=r... Write register n..., hex value without 0x, with value r..., @@ -1393,7 +1393,7 @@ } } break; - + case 'm': /* Read from memory. mAA..AA,LLLL AA..AA is the address and LLLL is the length. @@ -1416,7 +1416,7 @@ mem2hex(output_buffer, addr, len); } break; - + case 'X': /* Write to memory. XAA..AA,LLLL:XX..XX AA..AA is the start address, LLLL is the number of bytes, and @@ -1448,7 +1448,7 @@ } } break; - + case 'c': /* Continue execution. cAA..AA AA..AA is the address where execution is resumed. If AA..AA is @@ -1472,15 +1472,15 @@ if ((sreg.s0_3 & 0x3fff) == 0) { reg.ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT)); } - + return; - + case 's': /* Step. sAA..AA AA..AA is the address where execution is resumed. If AA..AA is omitted, resume at the present address. Success: return to the executing thread. Failure: will never know. */ - + if (input_buffer[1] != '\0') { /* FIXME: Doesn't handle address argument. */ gdb_cris_strcpy(output_buffer, error_message[E04]); @@ -1497,7 +1497,7 @@ return; case 'Z': - + /* Insert breakpoint or watchpoint, Ztype,addr,length. Remote protocol says: A remote target shall return an empty string for an unrecognized breakpoint or watchpoint packet type. */ @@ -1522,7 +1522,7 @@ int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16); int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16); char type = input_buffer[1]; - + remove_watchpoint(type, addr, len); break; } @@ -1537,14 +1537,14 @@ output_buffer[2] = lowhex(sigval); output_buffer[3] = 0; break; - + case 'D': /* Detach from host. D Success: OK, and return to the executing thread. Failure: will never know */ putpacket("OK"); return; - + case 'k': case 'r': /* kill request or reset request. @@ -1552,7 +1552,7 @@ Failure: will never know. */ kill_restart(); break; - + case 'C': case 'S': case '!': @@ -1570,7 +1570,7 @@ and ignored (below)? */ gdb_cris_strcpy(output_buffer, error_message[E04]); break; - + default: /* The stub should ignore other request and send an empty response ($#). This way we can extend the protocol and GDB @@ -1587,7 +1587,7 @@ { reg_intr_vect_rw_mask intr_mask; reg_ser_rw_intr_mask ser_intr_mask; - + /* Configure the kgdb serial port. */ #if defined(CONFIG_ETRAX_KGDB_PORT0) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1597,9 +1597,9 @@ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask.ser0 = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - + ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT1) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1609,9 +1609,9 @@ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask.ser1 = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - + ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT2) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1621,9 +1621,9 @@ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask.ser2 = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - + ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT3) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1635,7 +1635,7 @@ REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask); #endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb_asm.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb_asm.S --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb_asm.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb_asm.S 2006-10-13 14:43:13.000000000 +0200 @@ -11,7 +11,7 @@ .globl kgdb_handle_exception kgdb_handle_exception: - + ;; Create a register image of the caller. ;; ;; First of all, save the ACR on the stack since we need it for address calculations. @@ -262,7 +262,7 @@ ;; Nothing in S15, bank 3 clear.d [$acr] addq 4, $acr - + ;; Check what got us here: get IDX field of EXS. move $exs, $r10 and.d 0xff00, $r10 @@ -307,7 +307,7 @@ handle_comm: move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards jsr handle_exception ; Interactive routine - nop + nop ;; ;; Return to the caller @@ -345,7 +345,7 @@ ;; Nothing in S6 - S7, bank 0. addq 4, $acr addq 4, $acr - + move.d [$acr], $r0 move $r0, $s8 addq 4, $acr @@ -507,7 +507,7 @@ addq 8, $acr ;; Skip BZ, VR. - addq 2, $acr + addq 2, $acr move [$acr], $pid ; Restore PID addq 4, $acr diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/pinmux.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/pinmux.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/pinmux.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/pinmux.c 2006-08-11 10:32:21.000000000 +0200 @@ -1,7 +1,7 @@ -/* +/* * Allocator for I/O pins. All pins are allocated to GPIO at bootup. * Unassigned pins and GPIO pins can be allocated to a fixed interface - * or the I/O processor instead. + * or the I/O processor instead. * * Copyright (c) 2004 Axis Communications AB. */ @@ -33,9 +33,10 @@ if (!initialized) { reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); initialized = 1; - pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = - pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; + pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = + pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; REG_WR(pinmux, regi_pinmux, rw_pa, pa); crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); @@ -46,124 +47,137 @@ return 0; } -int -crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +/* + * must be called with the pinmux_lock held. + */ +static int __crisv32_pinmux_alloc(int port, int first_pin, int last_pin, + enum pin_mode mode) { int i; - unsigned long flags; - crisv32_pinmux_init(); - - if (port > PORTS) + if (port >= PORTS || + first_pin < 0 || last_pin >= PORT_PINS || last_pin < first_pin) return -EINVAL; - - spin_lock_irqsave(&pinmux_lock, flags); - - for (i = first_pin; i <= last_pin; i++) + + for (i = first_pin; i <= last_pin; i++) { - if ((pins[port][i] != pinmux_none) && (pins[port][i] != pinmux_gpio) && - (pins[port][i] != mode)) + if ((pins[port][i] != pinmux_none) + && (pins[port][i] != pinmux_gpio) + && (pins[port][i] != mode)) { - spin_unlock_irqrestore(&pinmux_lock, flags); #ifdef DEBUG panic("Pinmux alloc failed!\n"); #endif return -EPERM; } } - + for (i = first_pin; i <= last_pin; i++) pins[port][i] = mode; crisv32_pinmux_set(port); - - spin_unlock_irqrestore(&pinmux_lock, flags); - return 0; } int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int r; + unsigned long flags; + + crisv32_pinmux_init(); + + spin_lock_irqsave(&pinmux_lock, flags); + r = __crisv32_pinmux_alloc(port, first_pin, last_pin, mode); + spin_unlock_irqrestore(&pinmux_lock, flags); + return r; +} + +int crisv32_pinmux_alloc_fixed(enum fixed_function function) { int ret = -EINVAL; char saved[sizeof pins]; unsigned long flags; - + reg_pinmux_rw_hwprot hwprot; + + crisv32_pinmux_init(); + spin_lock_irqsave(&pinmux_lock, flags); /* Save internal data for recovery */ memcpy(saved, pins, sizeof pins); - - reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); - + + hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + switch(function) { case pinmux_ser1: - ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); hwprot.ser1 = regk_pinmux_yes; break; case pinmux_ser2: - ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); hwprot.ser2 = regk_pinmux_yes; break; case pinmux_ser3: - ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); hwprot.ser3 = regk_pinmux_yes; break; case pinmux_sser0: - ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); hwprot.sser0 = regk_pinmux_yes; break; case pinmux_sser1: - ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); hwprot.sser1 = regk_pinmux_yes; break; case pinmux_ata0: - ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); hwprot.ata0 = regk_pinmux_yes; break; case pinmux_ata1: - ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); hwprot.ata1 = regk_pinmux_yes; break; case pinmux_ata2: - ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); hwprot.ata2 = regk_pinmux_yes; break; case pinmux_ata3: - ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); hwprot.ata2 = regk_pinmux_yes; break; case pinmux_ata: - ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); - ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); + ret |= __crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); hwprot.ata = regk_pinmux_yes; break; case pinmux_eth1: - ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); hwprot.eth1 = regk_pinmux_yes; hwprot.eth1_mgm = regk_pinmux_yes; break; case pinmux_timer: - ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + ret = __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); hwprot.timer = regk_pinmux_yes; spin_unlock_irqrestore(&pinmux_lock, flags); return ret; } - + if (!ret) REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); else memcpy(pins, saved, sizeof pins); - - spin_unlock_irqrestore(&pinmux_lock, flags); - - return ret; + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; } void @@ -189,33 +203,126 @@ #endif } -int -crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +/* + * must be called with the pinmux_lock held. + */ +static int __crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) { int i; + + if (port > PORTS) + return -EINVAL; + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = pinmux_none; + + crisv32_pinmux_set(port); + + return 0; +} + +int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int r; unsigned long flags; crisv32_pinmux_init(); + + spin_lock_irqsave(&pinmux_lock, flags); + r = __crisv32_pinmux_dealloc(port, first_pin, last_pin); + spin_unlock_irqrestore(&pinmux_lock, flags); + return r; +} - if (port > PORTS) - return -EINVAL; +int +crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; spin_lock_irqsave(&pinmux_lock, flags); - for (i = first_pin; i <= last_pin; i++) - pins[port][i] = pinmux_none; + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch(function) + { + case pinmux_ser1: + ret = __crisv32_pinmux_dealloc(PORT_C, 4, 7); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = __crisv32_pinmux_dealloc(PORT_C, 8, 11); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = __crisv32_pinmux_dealloc(PORT_C, 12, 15); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_sser0: + ret = __crisv32_pinmux_dealloc(PORT_C, 0, 3); + ret |= __crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.sser0 = regk_pinmux_no; + break; + case pinmux_sser1: + ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4); + hwprot.sser1 = regk_pinmux_no; + break; + case pinmux_ata0: + ret = __crisv32_pinmux_dealloc(PORT_D, 5, 7); + ret |= __crisv32_pinmux_dealloc(PORT_D, 15, 17); + hwprot.ata0 = regk_pinmux_no; + break; + case pinmux_ata1: + ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4); + ret |= __crisv32_pinmux_dealloc(PORT_E, 17, 17); + hwprot.ata1 = regk_pinmux_no; + break; + case pinmux_ata2: + ret = __crisv32_pinmux_dealloc(PORT_C, 11, 15); + ret |= __crisv32_pinmux_dealloc(PORT_E, 3, 3); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata3: + ret = __crisv32_pinmux_dealloc(PORT_C, 8, 10); + ret |= __crisv32_pinmux_dealloc(PORT_C, 0, 2); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata: + ret = __crisv32_pinmux_dealloc(PORT_B, 0, 15); + ret |= __crisv32_pinmux_dealloc(PORT_D, 8, 15); + hwprot.ata = regk_pinmux_no; + break; + case pinmux_eth1: + ret = __crisv32_pinmux_dealloc(PORT_E, 0, 17); + hwprot.eth1 = regk_pinmux_no; + hwprot.eth1_mgm = regk_pinmux_no; + break; + case pinmux_timer: + ret = __crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.timer = regk_pinmux_no; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); - crisv32_pinmux_set(port); spin_unlock_irqrestore(&pinmux_lock, flags); - - return 0; + + return ret; } void crisv32_pinmux_dump(void) { int i, j; - + crisv32_pinmux_init(); for (i = 0; i < PORTS; i++) diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/process.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/process.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/process.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/process.c 2006-10-13 14:43:13.000000000 +0200 @@ -74,9 +74,9 @@ #else { reg_timer_rw_wd_ctrl wd_ctrl = {0}; - + stop_watchdog(); - + wd_ctrl.key = 16; /* Arbitrary key. */ wd_ctrl.cnt = 1; /* Minimum time. */ wd_ctrl.cmd = regk_timer_start; @@ -141,7 +141,7 @@ { struct pt_regs *childregs; struct switch_stack *swstack; - + /* * Put the pt_regs structure at the end of the new kernel stack page and * fix it up. Note: the task_struct doubles as the kernel stack for the @@ -152,7 +152,7 @@ p->set_child_tid = p->clear_child_tid = NULL; childregs->r10 = 0; /* Child returns 0 after a fork/clone. */ - /* Set a new TLS ? + /* Set a new TLS ? * The TLS is in $mof beacuse it is the 5th argument to sys_clone. */ if (p->mm && (clone_flags & CLONE_SETTLS)) { @@ -165,20 +165,20 @@ /* Paramater to ret_from_sys_call. 0 is don't restart the syscall. */ swstack->r9 = 0; - /* + /* * We want to return into ret_from_sys_call after the _resume. * ret_from_fork will call ret_from_sys_call. */ swstack->return_ip = (unsigned long) ret_from_fork; - + /* Fix the user-mode and kernel-mode stackpointer. */ - p->thread.usp = usp; + p->thread.usp = usp; p->thread.ksp = (unsigned long) swstack; return 0; } -/* +/* * Be aware of the "magic" 7th argument in the four system-calls below. * They need the latest stackframe, which is put as the 7th argument by * entry.S. The previous arguments are dummies or actually used, but need @@ -200,7 +200,7 @@ /* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ asmlinkage int -sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, +sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, unsigned long tls, long srp, struct pt_regs *regs) { if (!newusp) @@ -209,11 +209,11 @@ return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); } -/* +/* * vfork is a system call in i386 because of register-pressure - maybe * we can remove it and handle it in libc but we put it here until then. */ -asmlinkage int +asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { @@ -222,7 +222,7 @@ /* sys_execve() executes a new program. */ asmlinkage int -sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp, +sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp, struct pt_regs *regs) { int error; @@ -254,13 +254,13 @@ unsigned long usp = rdusp(); printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", regs->erp, regs->srp, regs->ccs, usp, regs->mof); - + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", regs->r0, regs->r1, regs->r2, regs->r3); - + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", regs->r4, regs->r5, regs->r6, regs->r7); - + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", regs->r8, regs->r9, regs->r10, regs->r11); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/ptrace.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/ptrace.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/ptrace.c 2006-03-22 10:56:56.000000000 +0100 @@ -20,7 +20,7 @@ #include #include -/* +/* * Determines which bits in CCS the user has access to. * 1 = access, 0 = no access. */ @@ -84,7 +84,7 @@ * * Make sure the single step bit is not set. */ -void +void ptrace_disable(struct task_struct *child) { unsigned long tmp; @@ -105,7 +105,7 @@ unsigned long __user *datap = (unsigned long __user *)data; switch (request) { - /* Read word at location address. */ + /* Read word at location address. */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: { unsigned long tmp; @@ -122,11 +122,11 @@ tmp = *(unsigned long*)addr; } else { copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - + if (copied != sizeof(tmp)) break; } - + ret = put_user(tmp,datap); break; } @@ -143,18 +143,18 @@ ret = put_user(tmp, datap); break; } - + /* Write the word at location address. */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: ret = 0; - + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; - + ret = -EIO; break; - + /* Write the word at location address in the USER area. */ case PTRACE_POKEUSR: ret = -EIO; @@ -178,10 +178,10 @@ case PTRACE_SYSCALL: case PTRACE_CONT: ret = -EIO; - + if (!valid_signal(data)) break; - + /* Continue means no single-step. */ put_reg(child, PT_SPC, 0); @@ -198,27 +198,27 @@ else { clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } - + child->exit_code = data; - + /* TODO: make sure any pending breakpoint is killed */ wake_up_process(child); ret = 0; - + break; - + /* Make the child exit by sending it a sigkill. */ case PTRACE_KILL: ret = 0; - + if (child->exit_state == EXIT_ZOMBIE) break; - + child->exit_code = SIGKILL; - + /* Deconfigure single-step and h/w bp. */ ptrace_disable(child); - + /* TODO: make sure any pending breakpoint is killed */ wake_up_process(child); break; @@ -227,7 +227,7 @@ case PTRACE_SINGLESTEP: { unsigned long tmp; ret = -EIO; - + /* Set up SPC if not set already (in which case we have no other choice but to trust it). */ if (!get_reg(child, PT_SPC)) { @@ -240,7 +240,7 @@ if (!valid_signal(data)) break; - + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); /* TODO: set some clever breakpoint mechanism... */ @@ -259,15 +259,15 @@ case PTRACE_GETREGS: { int i; unsigned long tmp; - + for (i = 0; i <= PT_MAX; i++) { tmp = get_reg(child, i); - + if (put_user(tmp, datap)) { ret = -EFAULT; goto out_tsk; } - + datap++; } @@ -279,22 +279,22 @@ case PTRACE_SETREGS: { int i; unsigned long tmp; - + for (i = 0; i <= PT_MAX; i++) { if (get_user(tmp, datap)) { ret = -EFAULT; goto out_tsk; } - + if (i == PT_CCS) { tmp &= CCS_MASK; tmp |= get_reg(child, PT_CCS) & ~CCS_MASK; } - + put_reg(child, i, tmp); datap++; } - + ret = 0; break; } @@ -304,6 +304,7 @@ break; } +out_tsk: return ret; } @@ -311,15 +312,15 @@ { if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; - + if (!(current->ptrace & PT_PTRACED)) return; - + /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); - + /* * This isn't the same as continuing with a signal, but it will do for * normal use. @@ -338,7 +339,7 @@ int copied; int opsize = 0; - /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */ + /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */ copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0); if (copied != sizeof(opcode)) return 0; @@ -361,7 +362,7 @@ opsize = 6; break; default: - panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n", + panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n", opcode, pc); } @@ -378,7 +379,7 @@ /* Delay slot bit set. Report as stopped on proper instruction. */ if (spc) { - /* Rely on SPC if set. FIXME: We might want to check + /* Rely on SPC if set. FIXME: We might want to check that EXS indicates we stopped due to a single-step exception. */ pc = spc; @@ -422,7 +423,7 @@ register int old_srs; #ifdef CONFIG_ETRAX_KGDB - /* Ignore write, but pretend it was ok if value is 0 + /* Ignore write, but pretend it was ok if value is 0 (we don't want POKEUSR/SETREGS failing unnessecarily). */ return (data == 0) ? ret : -1; #endif @@ -431,7 +432,7 @@ if (!bp_owner) bp_owner = pid; else if (bp_owner != pid) { - /* Ignore write, but pretend it was ok if value is 0 + /* Ignore write, but pretend it was ok if value is 0 (we don't want POKEUSR/SETREGS failing unnessecarily). */ return (data == 0) ? ret : -1; } @@ -440,7 +441,7 @@ SPEC_REG_RD(SPEC_REG_SRS, old_srs); /* Switch to BP bank. */ SUPP_BANK_SEL(BANK_BP); - + switch (regno - PT_BP) { case 0: SUPP_REG_WR(0, data); break; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/setup.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/setup.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/setup.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/setup.c 2006-10-13 14:43:13.000000000 +0200 @@ -40,12 +40,12 @@ {"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG}, - + {"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU}, - + {"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU}, - + {"Unknown", 0, 0, 0} }; @@ -67,7 +67,7 @@ #endif revision = rdvr(); - + for (i = 0; i < entries; i++) { if (cpinfo[i].rev == revision) { info = &cpinfo[i]; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/signal.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/signal.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/signal.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/signal.c 2006-03-22 10:56:56.000000000 +0100 @@ -50,7 +50,7 @@ unsigned char retcode[8]; /* Trampoline code. */ }; -int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs); +void do_signal(int restart, struct pt_regs *regs); void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs); /* @@ -61,74 +61,16 @@ sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - sigset_t saveset; - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); - recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* - * This point is reached twice: once to call - * the signal handler, then again to return - * from the sigsuspend system call. When - * calling the signal handler, R10 hold the - * signal number as set by do_signal(). The - * sigsuspend call will always return with - * the restored value above; -EINTR. - */ - return regs->r10; - } - } -} - -/* Define some dummy arguments to be able to reach the regs argument. */ -int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, - long mof, long srp, struct pt_regs *regs) -{ - sigset_t saveset; - sigset_t newset; - - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - current->blocked = newset; - - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* See comment in function above. */ - return regs->r10; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } int @@ -263,7 +205,7 @@ unsigned long oldccs = regs->ccs; frame = (struct rt_signal_frame *) rdusp(); - + /* * Since the signal is stacked on a dword boundary, the frame * should be dword aligned here as well. It it's not, then the @@ -285,7 +227,7 @@ recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; @@ -311,7 +253,7 @@ err = 0; usp = rdusp(); - + /* * Copy the registers. They are located first in sc, so it's * possible to use sc directly. @@ -351,7 +293,7 @@ * which performs the syscall sigreturn(), or a provided user-mode * trampoline. */ -static void +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { @@ -388,7 +330,7 @@ /* Trampoline - the desired return ip is in the signal return page. */ return_ip = cris_signal_return_page; - /* + /* * This is movu.w __NR_sigreturn, r9; break 13; * * WE DO NOT USE IT ANY MORE! It's only left here for historical @@ -402,7 +344,7 @@ if (err) goto give_sigsegv; - + /* * Set up registers for signal handler. * @@ -417,16 +359,17 @@ /* Actually move the USP to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; - + force_sig(SIGSEGV, current); + return -EFAULT; } -static void +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { @@ -441,11 +384,11 @@ goto give_sigsegv; /* TODO: what is the current->exec_domain stuff and invmap ? */ - + err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, info); - + if (err) goto give_sigsegv; @@ -467,7 +410,7 @@ /* Trampoline - the desired return ip is in the signal return page. */ return_ip = cris_signal_return_page + 6; - /* + /* * This is movu.w __NR_rt_sigreturn, r9; break 13; * * WE DO NOT USE IT ANY MORE! It's only left here for historical @@ -478,7 +421,7 @@ err |= __put_user(__NR_rt_sigreturn, (short __user*)(frame->retcode+2)); - + err |= __put_user(0xe93d, (short __user*)(frame->retcode+4)); } @@ -503,21 +446,24 @@ /* Actually move the usp to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; - + force_sig(SIGSEGV, current); + return -EFAULT; } /* Invoke a singal handler to, well, handle the signal. */ -static inline void +static inline int handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Check if this got called from a system call. */ if (canrestart) { /* If so, check system call restarting. */ @@ -561,19 +507,23 @@ /* Set up the stack frame. */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -587,12 +537,13 @@ * we can use user_mode(regs) to see if we came directly from kernel or user * mode below. */ -int -do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +void +do_signal(int canrestart, struct pt_regs *regs) { int signr; siginfo_t info; struct k_sigaction ka; + sigset_t *oldset; /* * The common case should go fast, which is why this point is @@ -600,17 +551,27 @@ * without doing anything. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); - + if (signr > 0) { - /* Deliver the signal. */ - handle_signal(canrestart, signr, &info, &ka, oldset, regs); - return 1; + /* Whee! Actually deliver the signal. */ + if (handle_signal(canrestart, signr, &info, &ka, oldset, regs)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; } /* Got here from a system call? */ @@ -621,14 +582,19 @@ regs->r10 == -ERESTARTNOINTR) { RESTART_CRIS_SYS(regs); } - + if (regs->r10 == -ERESTART_RESTARTBLOCK){ regs->r10 = __NR_restart_syscall; regs->erp -= 2; } } - - return 0; + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } asmlinkage void @@ -651,7 +617,7 @@ sys_kill(ti->task->pid, sig); } -void +void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs) { @@ -666,7 +632,7 @@ regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT)); /* Assume the SPC is valid and interesting. */ regs->spc = oldspc; - + } else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) { /* If a h/w bp was set in the signal handler we need to keep the S flag. */ @@ -679,7 +645,7 @@ have forgotten all about it. */ regs->spc = 0; regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT)); - } + } } /* Set up the trampolines on the signal return page. */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/smp.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/smp.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/smp.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/smp.c 2007-01-09 10:29:19.000000000 +0100 @@ -20,6 +20,7 @@ #define IPI_SCHEDULE 1 #define IPI_CALL 2 #define IPI_FLUSH_TLB 4 +#define IPI_BOOT 8 #define FLUSH_ALL (void*)0xffffffff @@ -30,6 +31,8 @@ cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); cpumask_t phys_cpu_present_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map; +EXPORT_SYMBOL(cpu_possible_map); EXPORT_SYMBOL(phys_cpu_present_map); /* Variables used during SMP boot */ @@ -55,7 +58,7 @@ extern int setup_irq(int, struct irqaction *); /* Mode registers */ -static unsigned long irq_regs[NR_CPUS] = +static unsigned long irq_regs[NR_CPUS] = { regi_irq, regi_irq2 @@ -97,6 +100,7 @@ cpu_set(0, cpu_online_map); cpu_set(0, phys_cpu_present_map); + cpu_set(0, cpu_possible_map); } void __init smp_cpus_done(unsigned int max_cpus) @@ -109,6 +113,7 @@ { unsigned timeout; struct task_struct *idle; + cpumask_t cpu_mask = CPU_MASK_NONE; idle = fork_idle(cpuid); if (IS_ERR(idle)) @@ -120,6 +125,12 @@ smp_init_current_idle_thread = task_thread_info(idle); cpu_now_booting = cpuid; + /* Kick it */ + cpu_set(cpuid, cpu_online_map); + cpu_set(cpuid, cpu_mask); + send_ipi(IPI_BOOT, 0, cpu_mask); + cpu_clear(cpuid, cpu_online_map); + /* Wait for CPU to come online */ for (timeout = 0; timeout < 10000; timeout++) { if(cpu_online(cpuid)) { @@ -142,7 +153,7 @@ * specific stuff such as the local timer and the MMU. */ void __init smp_callin(void) { - extern void cpu_idle(void); + extern void cpu_idle(void); int cpu = cpu_now_booting; reg_intr_vect_rw_mask vect_mask = {0}; @@ -190,8 +201,8 @@ /* cache_decay_ticks is used by the scheduler to decide if a process * is "hot" on one CPU. A higher value means a higher penalty to move - * a process to another CPU. Our cache is rather small so we report - * 1 tick. + * a process to another CPU. Our cache is rather small so we report + * 1 tick. */ unsigned long cache_decay_ticks = 1; @@ -205,14 +216,14 @@ { cpumask_t cpu_mask = CPU_MASK_NONE; cpu_set(cpu, cpu_mask); - send_ipi(IPI_SCHEDULE, 0, cpu_mask); + send_ipi(IPI_SCHEDULE, 0, cpu_mask); } /* TLB flushing * * Flush needs to be done on the local CPU and on any other CPU that * may have the same mapping. The mm->cpu_vm_mask is used to keep track - * of which CPUs that a specific process has been executed on. + * of which CPUs that a specific process has been executed on. */ void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr) { @@ -244,7 +255,7 @@ cpu_set(smp_processor_id(), mm->cpu_vm_mask); } -void flush_tlb_page(struct vm_area_struct *vma, +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { __flush_tlb_page(vma, addr); @@ -252,8 +263,8 @@ } /* Inter processor interrupts - * - * The IPIs are used for: + * + * The IPIs are used for: * * Force a schedule on a CPU * * FLush TLB on other CPUs * * Call a function on other CPUs @@ -341,7 +352,7 @@ else if (flush_vma == FLUSH_ALL) __flush_tlb_mm(flush_mm); else - __flush_tlb_page(flush_vma, flush_addr); + __flush_tlb_page(flush_vma, flush_addr); } ipi.vector = 0; diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/time.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/time.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/time.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/time.c 2007-01-09 10:29:19.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $ +/* $Id: time.c,v 1.25 2007/01/09 09:29:19 starvik Exp $ * * linux/arch/cris/arch-v32/kernel/time.c * @@ -14,12 +14,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -31,7 +33,7 @@ #define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ #define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */ -unsigned long timer_regs[NR_CPUS] = +unsigned long timer_regs[NR_CPUS] = { regi_timer, #ifdef CONFIG_SMP @@ -44,6 +46,15 @@ extern int setup_irq(int, struct irqaction *); extern int have_rtc; +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, void *data); + +static struct notifier_block cris_time_freq_notifier_block = { + .notifier_call = cris_time_freq_notifier +}; +#endif + unsigned long get_ns_in_jiffie(void) { reg_timer_r_tmr0_data data; @@ -63,7 +74,7 @@ static unsigned long jiffies_p = 0; /* - * cache volatile jiffies temporarily; we have IRQs turned off. + * cache volatile jiffies temporarily; we have IRQs turned off. */ unsigned long jiffies_t; @@ -82,7 +93,7 @@ */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { - /* Timer wrapped, use new count and prescale + /* Timer wrapped, use new count and prescale * increase the time corresponding to one jiffie */ usec_count = 1000000/HZ; @@ -101,7 +112,7 @@ * The watchdog timer is an 8-bit timer with a configurable start value. * Once started the whatchdog counts downwards with a frequency of 763 Hz * (100/131072 MHz). When the watchdog counts down to 1, it generates an - * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the + * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the * chip. */ /* This gives us 1.3 ms to do something useful when the NMI comes */ @@ -124,7 +135,7 @@ { #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; - + /* only keep watchdog happy as long as we have memory left! */ if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { /* reset the watchdog with the inverse of the old key */ @@ -139,7 +150,7 @@ /* stop the watchdog - we still need the correct key */ -void +void stop_watchdog(void) { #if defined(CONFIG_ETRAX_WATCHDOG) @@ -149,7 +160,7 @@ wd_ctrl.cmd = regk_timer_stop; wd_ctrl.key = watchdog_key; REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); -#endif +#endif } extern void show_registers(struct pt_regs *regs); @@ -160,7 +171,8 @@ #if defined(CONFIG_ETRAX_WATCHDOG) extern int cause_of_death; - raw_printk("Watchdog bite\n"); + oops_in_progress = 1; + printk("Watchdog bite\n"); /* Check if forced restart or unexpected watchdog */ if (cause_of_death == 0xbedead) { @@ -169,8 +181,9 @@ /* Unexpected watchdog, stop the watchdog and dump registers*/ stop_watchdog(); - raw_printk("Oops: bitten by watchdog\n"); - show_registers(regs); + printk("Oops: bitten by watchdog\n"); + show_registers(regs); + oops_in_progress = 0; #ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY reset_watchdog(); #endif @@ -191,8 +204,9 @@ extern void cris_do_profile(struct pt_regs *regs); static inline irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_interrupt(int irq, void *dev_id) { + struct pt_regs* regs = get_irq_regs(); int cpu = smp_processor_id(); reg_timer_r_masked_intr masked_intr; reg_timer_rw_ack_intr ack_intr = { 0 }; @@ -226,7 +240,7 @@ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. * - * The division here is not time critical since it will run once in + * The division here is not time critical since it will run once in * 11 minutes */ if ((time_status & STA_UNSYNC) == 0 && @@ -246,7 +260,7 @@ */ static struct irqaction irq_timer = { - .mask = timer_interrupt, + .handler = timer_interrupt, .flags = IRQF_SHARED | IRQF_DISABLED, .mask = CPU_MASK_NONE, .name = "timer" @@ -262,7 +276,7 @@ /* Setup the etrax timers * Base frequency is 100MHz, divider 1000000 -> 100 HZ - * We use timer0, so timer1 is free. + * We use timer0, so timer1 is free. * The trig timer is used by the fasttimer API if enabled. */ @@ -284,11 +298,11 @@ { reg_intr_vect_rw_mask intr_mask; - /* probe for the RTC and read it if it exists - * Before the RTC can be probed the loops_per_usec variable needs - * to be initialized to make usleep work. A better value for - * loops_per_usec is calculated by the kernel later once the - * clock has started. + /* probe for the RTC and read it if it exists + * Before the RTC can be probed the loops_per_usec variable needs + * to be initialized to make usleep work. A better value for + * loops_per_usec is calculated by the kernel later once the + * clock has started. */ loops_per_usec = 50; @@ -297,7 +311,7 @@ xtime.tv_sec = 0; xtime.tv_nsec = 0; have_rtc = 0; - } else { + } else { /* get the current time */ have_rtc = 1; update_xtime_from_cmos(); @@ -316,9 +330,9 @@ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask.timer = 1; REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - + /* now actually register the timer irq handler that calls timer_interrupt() */ - + setup_irq(TIMER_INTR_VECT, &irq_timer); /* enable watchdog if we should use one */ @@ -330,10 +344,7 @@ /* If we use the hardware watchdog, we want to trap it as an NMI and dump registers before it resets us. For this to happen, we must set the "m" NMI enable flag (which once set, is unset only - when an NMI is taken). - - The same goes for the external NMI, but that doesn't have any - driver or infrastructure support yet. */ + when an NMI is taken). */ { unsigned long flags; local_save_flags(flags); @@ -341,4 +352,27 @@ local_irq_restore(flags); } #endif + +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&cris_time_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); +#endif +} + +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_POSTCHANGE) { + reg_timer_r_tmr0_data data; + reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ; + do + { + data = REG_RD(timer, timer_regs[freqs->cpu], r_tmr0_data); + } while (data > 20); + REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div); + } + return 0; } +#endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/traps.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/traps.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/traps.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/traps.c 2006-12-11 14:04:24.000000000 +0100 @@ -1,50 +1,43 @@ /* - * Copyright (C) 2003, Axis Communications AB. + * Copyright (C) 2003-2006, Axis Communications AB. */ #include #include - #include - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - -extern int raw_printk(const char *fmt, ...); +#include void show_registers(struct pt_regs *regs) { /* * It's possible to use either the USP register or current->thread.usp. - * USP might not correspond to the current proccess for all cases this + * USP might not correspond to the current process for all cases this * function is called, and current->thread.usp isn't up to date for the - * current proccess. Experience shows that using USP is the way to go. + * current process. Experience shows that using USP is the way to go. */ - unsigned long usp; + unsigned long usp = rdusp(); unsigned long d_mmu_cause; unsigned long i_mmu_cause; - usp = rdusp(); + printk("CPU: %d\n", smp_processor_id()); - raw_printk("CPU: %d\n", smp_processor_id()); + printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); - raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", - regs->erp, regs->srp, regs->ccs, usp, regs->mof); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", - regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", - regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", - regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs->acr); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs->acr); - - raw_printk("sp: %08lx\n", regs); + printk(" sp: %08lx\n", (unsigned long)regs); SUPP_BANK_SEL(BANK_IM); SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause); @@ -52,18 +45,20 @@ SUPP_BANK_SEL(BANK_DM); SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause); - raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause); - raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); + printk(" Data MMU Cause: %08lx\n", d_mmu_cause); + printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); - raw_printk("Process %s (pid: %d, stackpage: %08lx)\n", - current->comm, current->pid, (unsigned long) current); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); - /* Show additional info if in kernel-mode. */ + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ if (!user_mode(regs)) { int i; - unsigned char c; - show_stack(NULL, (unsigned long *) usp); + show_stack(NULL, (unsigned long *)usp); /* * If the previous stack-dump wasn't a kernel one, dump the @@ -72,7 +67,7 @@ if (usp != 0) show_stack(NULL, NULL); - raw_printk("\nCode: "); + printk("\nCode: "); if (regs->erp < PAGE_OFFSET) goto bad_value; @@ -84,76 +79,65 @@ * instruction decoding should be in sync at the interesting * point, but small enough to fit on a row. The regs->erp * location is pointed out in a ksymoops-friendly way by - * wrapping the byte for that address in parenthesis. + * wrapping the byte for that address in parenthesises. */ for (i = -12; i < 12; i++) { - if (__get_user(c, &((unsigned char *) regs->erp)[i])) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->erp)[i])) { bad_value: - raw_printk(" Bad IP value."); + printk(" Bad IP value."); break; } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); + printk("%02x ", c); } - - raw_printk("\n"); + printk("\n"); } } -/* - * This gets called from entry.S when the watchdog has bitten. Show something - * similiar to an Oops dump, and if the kernel if configured to be a nice doggy; - * halt instead of reboot. - */ void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); - - while (1) - ; /* Do nothing. */ -#else - show_registers(regs); -#endif + unsigned long flags; + + local_save_flags(flags); + flags |= (1 << 30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); } -/* This is normally the Oops function. */ -void -die_if_kernel(const char *str, struct pt_regs *regs, long err) +extern void (*nmi_handler)(struct pt_regs*); +void handle_nmi(struct pt_regs* regs) { - if (user_mode(regs)) - return; + reg_intr_vect_r_nmi r; -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* - * This printout might take too long and could trigger - * the watchdog normally. If NICE_DOGGY is set, simply - * stop the watchdog during the printout. - */ - stop_watchdog(); -#endif - - raw_printk("%s: %04lx\n", str, err & 0xffff); + if (nmi_handler) + nmi_handler(regs); - show_registers(regs); - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif - - do_exit(SIGSEGV); + /* Wait until nmi is no longer active. */ + do { + r = REG_RD(intr_vect, regi_irq, r_nmi); + } while (r.ext == regk_intr_vect_on); } -void arch_enable_nmi(void) +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); + struct bug_frame f; + unsigned char c; + unsigned long erp = regs->erp; + + if (__copy_from_user(&f, (const void __user *)(erp - 8), sizeof f)) + return; + if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC) + return; + if (__get_user(c, f.filename)) + f.filename = ""; + + printk("kernel BUG at %s:%d!\n", f.filename, f.line); } +#endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.c --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,96 +0,0 @@ -// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook. This is the part running in the -// simulated program. -// - -#include "vcs_hook.h" -#include -#include -#include - -#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ -#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ - -#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0) -#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset] - - -// ------------------------------------------------------------------ hook_call -int hook_call( unsigned id, unsigned pcnt, ...) { - va_list ap; - unsigned i; - unsigned ret; -#ifdef USING_SOS - PREEMPT_OFF_SAVE(); -#endif - - // pass parameters - HOOK_DATA(0) = id; - - /* Have to make hook_print_str a special case since we call with a - parameter of byte type. Should perhaps be a separate - hook_call. */ - - if (id == hook_print_str) { - int i; - char *str; - - HOOK_DATA(1) = pcnt; - - va_start(ap, pcnt); - str = (char*)va_arg(ap,unsigned); - - for (i=0; i!=pcnt; i++) { - HOOK_DATA_BYTE(8+i) = str[i]; - } - HOOK_DATA_BYTE(8+i) = 0; /* null byte */ - } - else { - va_start(ap, pcnt); - for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned); - va_end(ap); - } - - // read from mem to make sure data has propagated to memory before trigging - *((volatile unsigned*) HOOK_MEM_BASE_ADDR); - - // trigger hook - HOOK_TRIG(id); - - // wait for call to finish - while( VHOOK_DATA(0) > 0 ) {} - - // extract return value - - ret = VHOOK_DATA(1); - -#ifdef USING_SOS - PREEMPT_RESTORE(); -#endif - return ret; -} - -unsigned -hook_buf(unsigned i) -{ - return (HOOK_DATA(i)); -} - -void print_str( const char *str ) { - int i; - for (i=1; str[i]; i++); /* find null at end of string */ - hook_call(hook_print_str, i, str); -} - -// --------------------------------------------------------------- CPU_KICK_DOG -void CPU_KICK_DOG(void) { - (void) hook_call( hook_kick_dog, 0 ); -} - -// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT -void CPU_WATCHDOG_TIMEOUT( unsigned t ) { - (void) hook_call( hook_dog_timeout, 1, t ); -} diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.h linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.h --- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.h 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,42 +0,0 @@ -// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook functions - -#ifndef HOOK_H -#define HOOK_H - -int hook_call( unsigned id, unsigned pcnt, ...); - -enum hook_ids { - hook_debug_on = 1, - hook_debug_off, - hook_stop_sim_ok, - hook_stop_sim_fail, - hook_alloc_shared, - hook_ptr_shared, - hook_free_shared, - hook_file2shared, - hook_cmp_shared, - hook_print_params, - hook_sim_time, - hook_stop_sim, - hook_kick_dog, - hook_dog_timeout, - hook_rand, - hook_srand, - hook_rand_range, - hook_print_str, - hook_print_hex, - hook_cmp_offset_shared, - hook_fill_random_shared, - hook_alloc_random_data, - hook_calloc_random_data, - hook_print_int, - hook_print_uint, - hook_fputc, - hook_init_fd, - hook_sbrk - -}; - -#endif diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/lib/Makefile --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/Makefile 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/Makefile 2006-10-11 19:29:20.000000000 +0200 @@ -2,5 +2,5 @@ # Makefile for Etrax-specific library files.. # -lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o +lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o delay.o diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksum.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksum.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksum.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksum.S 2005-08-15 15:53:12.000000000 +0200 @@ -7,15 +7,15 @@ .globl csum_partial csum_partial: - + ;; r10 - src ;; r11 - length ;; r12 - checksum ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the + ;; we also do _NOT_ want to compute a checksum over more than the ;; actual length when length < 40 - + cmpu.w 80,$r11 blo _word_loop nop @@ -24,17 +24,17 @@ ;; this overhead is why we have a check above for breakeven length ;; only r0 - r8 have to be saved, the other ones are clobber-able ;; according to the ABI - + subq 9*4,$sp subq 10*4,$r11 ; update length for the first loop movem $r8,[$sp] - + ;; do a movem checksum _mloop: movem [$r10+],$r9 ; read 10 longwords ;; perform dword checksumming on the 10 longwords - + add.d $r0,$r12 addc $r1,$r12 addc $r2,$r12 @@ -48,9 +48,8 @@ ;; fold the carry into the checksum, to avoid having to loop the carry ;; back into the top - + addc 0,$r12 - addc 0,$r12 ; do it again, since we might have generated a carry subq 10*4,$r11 bge _mloop @@ -68,34 +67,30 @@ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below. ;; r9 and r13 can be used as temporaries. - + moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9 lsrq 16,$r9 - + move.d $r12,$r13 lsrq 16,$r13 ; r13 = checksum >> 16 and.d $r9,$r12 ; checksum = checksum & 0xffff add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 _no_fold: cmpq 2,$r11 blt _no_words nop - + ;; checksum the rest of the words - + subq 2,$r11 - + _wloop: subq 2,$r11 bge _wloop addu.w [$r10+],$r12 - + addq 2,$r11 - + _no_words: ;; see if we have one odd byte more cmpq 1,$r11 @@ -104,7 +99,7 @@ ret move.d $r12,$r10 -_do_byte: +_do_byte: ;; copy and checksum the last byte addu.b [$r10],$r12 ret diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksumcopy.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksumcopy.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksumcopy.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksumcopy.S 2005-08-15 15:53:12.000000000 +0200 @@ -3,23 +3,23 @@ * Copyright (c) 1998, 2001, 2003 Axis Communications AB * * Authors: Bjorn Wesen - * + * * csum_partial_copy_nocheck(const char *src, char *dst, * int len, unsigned int sum) */ .globl csum_partial_copy_nocheck -csum_partial_copy_nocheck: - +csum_partial_copy_nocheck: + ;; r10 - src ;; r11 - dst ;; r12 - length ;; r13 - checksum ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the + ;; we also do _NOT_ want to compute a checksum over more than the ;; actual length when length < 40 - + cmpu.w 80,$r12 blo _word_loop nop @@ -28,19 +28,19 @@ ;; this overhead is why we have a check above for breakeven length ;; only r0 - r8 have to be saved, the other ones are clobber-able ;; according to the ABI - + subq 9*4,$sp subq 10*4,$r12 ; update length for the first loop movem $r8,[$sp] - + ;; do a movem copy and checksum - + 1: ;; A failing userspace access (the read) will have this as PC. _mloop: movem [$r10+],$r9 ; read 10 longwords movem $r9,[$r11+] ; write 10 longwords ;; perform dword checksumming on the 10 longwords - + add.d $r0,$r13 addc $r1,$r13 addc $r2,$r13 @@ -54,9 +54,8 @@ ;; fold the carry into the checksum, to avoid having to loop the carry ;; back into the top - + addc 0,$r13 - addc 0,$r13 ; do it again, since we might have generated a carry subq 10*4,$r12 bge _mloop @@ -74,34 +73,30 @@ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below ;; r9 can be used as temporary. - + move.d $r13,$r9 lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 - + _no_fold: cmpq 2,$r12 blt _no_words nop - + ;; copy and checksum the rest of the words - + subq 2,$r12 - + 2: ;; A failing userspace access for the read below will have this as PC. _wloop: move.w [$r10+],$r9 addu.w $r9,$r13 subq 2,$r12 bge _wloop move.w $r9,[$r11+] - + addq 2,$r12 - + _no_words: ;; see if we have one odd byte more cmpq 1,$r12 @@ -110,7 +105,7 @@ ret move.d $r13,$r10 -_do_byte: +_do_byte: ;; copy and checksum the last byte 3: ;; A failing userspace access for the read below will have this as PC. move.b [$r10],$r9 diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/delay.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/delay.c --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/delay.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/delay.c 2006-10-16 01:08:41.000000000 +0200 @@ -0,0 +1,28 @@ +/* + * Precise Delay Loops for ETRAX FS + * + * Copyright (C) 2006 Axis Communications AB. + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * On ETRAX FS, we can check the free-running read-only 100MHz timer + * getting 32-bit 10ns precision, theoretically good for 42.94967295 + * seconds. Unsigned arithmetic and careful expression handles + * wrapping. + */ + +void cris_delay10ns(u32 n10ns) +{ + u32 t0 = REG_RD(timer, regi_timer, r_time); + while (REG_RD(timer, regi_timer, r_time) - t0 < n10ns) + ; +} +EXPORT_SYMBOL(cris_delay10ns); diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/dram_init.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/dram_init.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/dram_init.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/dram_init.S 2006-11-28 11:06:19.000000000 +0100 @@ -1,14 +1,14 @@ -/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $ - * +/* $Id: dram_init.S,v 1.9 2006/11/28 10:06:19 ricardw Exp $ + * * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * - * Note: This file may not modify r8 or r9 because they are used to - * carry information from the decompresser to the kernel + * Note: This file may not modify r8 .. r12 because they are used to + * carry information from the decompressor to the kernel * * Copyright (C) 2000-2003 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik (starvik@axis.com) */ /* Just to be certain the config file is included, we include it here @@ -18,13 +18,13 @@ #include #include - - ;; WARNING! The registers r8 and r9 are used as parameters carrying - ;; information from the decompressor (if the kernel was compressed). + + ;; WARNING! The registers r8 .. r12 are used as parameters carrying + ;; information from the decompressor (if the kernel was compressed). ;; They should not be used in the code below. ; Refer to BIF MDS for a description of SDRAM initialization - + ; Bank configuration move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0), $r0 move.d CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1 @@ -33,7 +33,7 @@ move.d CONFIG_ETRAX_SDRAM_GRP1_CONFIG, $r1 move.d $r1, [$r0] - ; Calculate value of mrs_data + ; 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 @@ -43,7 +43,7 @@ move.d CONFIG_ETRAX_SDRAM_COMMAND, $r2 bne _set_timing nop - + move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2 move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 and.d 0x07, $r1 ; Get CAS latency @@ -51,7 +51,7 @@ beq _bw_check nop move.d 0x60, $r4 - + _bw_check: ; Assume that group 0 width is equal to group 1. This assumption ; is wrong for a group 1 only hardware (such as the grand old @@ -67,23 +67,27 @@ move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 and.d ~(3 << reg_bif_core_rw_sdram_timing___ref___lsb), $r1 move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0 - move.d $r1, [$r0] + move.d $r1, [$r0] + + ; Wait 200us + move.d 10000, $r2 +1: bne 1b + subq 1, $r2 ; Issue NOP command move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd), $r5 moveq regk_bif_core_nop, $r1 move.d $r1, [$r5] - + ; Wait 200us move.d 10000, $r2 1: bne 1b subq 1, $r2 - + ; Issue initialization command sequence - move.d _sdram_commands_start, $r2 - and.d 0x000fffff, $r2 ; Make sure commands are read from flash - move.d _sdram_commands_end, $r3 - and.d 0x000fffff, $r3 + lapc.d _sdram_commands_start, $r2 ; position-independent + lapc.d _sdram_commands_end, $r3 ; position-independent + 1: clear.d $r6 move.b [$r2+], $r6 ; Load command or.d $r4, $r6 ; Add calculated mrs @@ -100,7 +104,7 @@ move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0 move.d $r1, [$r0] - + ; Initialization finished ba _sdram_commands_end nop @@ -116,4 +120,4 @@ .byte regk_bif_core_ref ; refresh .byte regk_bif_core_ref ; refresh .byte regk_bif_core_mrs ; mrs -_sdram_commands_end: +_sdram_commands_end: diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/hw_settings.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/hw_settings.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/hw_settings.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/hw_settings.S 2006-10-13 14:43:15.000000000 +0200 @@ -1,25 +1,25 @@ /* - * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $ - * + * $Id: hw_settings.S,v 1.5 2006/10/13 12:43:15 starvik Exp $ + * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. * Don't forget to update the tools if you change this table. * * Copyright (C) 2001 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik (starvik@axis.com) */ #include #include #include - + .ascii "HW_PARAM_MAGIC" ; Magic number .dword 0xc0004000 ; Kernel start address ; Debug port #ifdef CONFIG_ETRAX_DEBUG_PORT0 - .dword 0 + .dword 0 #elif defined(CONFIG_ETRAX_DEBUG_PORT1) .dword 1 #elif defined(CONFIG_ETRAX_DEBUG_PORT2) @@ -28,9 +28,9 @@ .dword 3 #else .dword 4 ; No debug -#endif +#endif - ; Register values + ; Register values .dword REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg) .dword CONFIG_ETRAX_MEM_GRP1_CONFIG .dword REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg) diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/memset.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/memset.c --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/memset.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/memset.c 2003-07-02 05:00:14.000000000 +0200 @@ -66,7 +66,7 @@ { register char *dst __asm__ ("r13") = pdst; - + /* This is NONPORTABLE, but since this whole routine is */ /* grossly nonportable that doesn't matter. */ @@ -156,7 +156,7 @@ } /* Either we directly starts copying, using dword copying - in a loop, or we copy as much as possible with 'movem' + in a loop, or we copy as much as possible with 'movem' and then the last block (<44 bytes) is copied here. This will work since 'movem' will have updated src,dst,n. */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/nand_init.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/nand_init.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/nand_init.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/nand_init.S 2007-01-31 16:52:19.000000000 +0100 @@ -9,14 +9,16 @@ ## ## Some notes about the bug/feature for future reference: ## The bootrom copies the first 127 KB from NAND flash to internal -## memory. The problem is that it does a bytewise copy. NAND flashes -## does autoincrement on the address so for a 16-bite device each -## read/write increases the address by two. So the copy loop in the +## memory. The problem is that it does a bytewise copy, copying +## a single byte from the lowest byte of the bus for each address. +## NAND flashes autoincrement on the address so for a 16 bit device +## each read/write increases the address by two. So the copy loop in the ## bootrom will discard every second byte. This is solved by inserting -## zeroes in every second byte in the first erase block. +## zeroes in every second byte in the first erase block, in order +## to get contiguous code. ## ## The bootrom also incorrectly assumes that it can read the flash -## linear with only one read command but the flash will actually +## linearly with only one read command but the flash will actually ## switch between normal area and spare area if you do that so we ## can't trust more than the first 256 bytes. ## @@ -29,14 +31,16 @@ #include ;; There are 8-bit NAND flashes and 16-bit NAND flashes. -;; We need to treat them slightly different. -#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 -#define PAGE_SIZE 256 +;; We need to treat them slightly differently. +#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2 +#define PAGE_SIZE_ADDRESSES 256 #else -#error 2 -#define PAGE_SIZE 512 +#define PAGE_SIZE_ADDRESSES 512 #endif + +;; Block size for erase #define ERASE_BLOCK 16384 +#define PAGE_SIZE_BYTES 512 ;; GPIO pins connected to NAND flash #define CE 4 @@ -49,6 +53,7 @@ #define NAND_WR_ADDR 0x94000000 #define READ_CMD 0x00 +#define RESET_CMD 0xFF ;; Readability macros #define CSP_MASK \ @@ -58,6 +63,10 @@ REG_STATE(bif_core, rw_grp3_cfg, gated_csp0, rd) | \ REG_STATE(bif_core, rw_grp3_cfg, gated_csp1, wr) +;; Normally we initialize GPIO and bus interfaces. +;; This is strictly not necessary; boot ROM does this for us. +#define INTERFACE_SETUP (1) + ;;---------------------------------------------------------------------------- ;; Macros to set/clear GPIO bits @@ -71,16 +80,41 @@ move.d $r9, [$r2] .endm +.macro GPIO_SYNC +;; Originally, we read back data written to nand flash in order +;; to flush the pipeline. It turned out however, that the real +;; culprit was a lack of wait states. +;; This macro remains in the code however in case this conclusion +;; is wrong too. +;; +;; move.d [$r2], $r9 ; read back to flush pipeline +.endm + ;;---------------------------------------------------------------------------- +;; Read value from bus to temporary register to sync with previous write +;; This generates no signal to the NAND flash, since only chip select lines are +;; pulled out to the chip, and read is not gated with chip select for the write +;; area. -nand_boot: - ;; Check if nand boot was selected - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne normal_boot ; No NAND boot - nop +.macro BUS_SYNC r + move.b [$r1], \r +.endm + +;;---------------------------------------------------------------------------- +;; Delay macro +;; x = delay = 10*x + 20 ns, e.g. DELAY 25 => 270ns delay, max 63 (650 ns) +;;(@200Mc) +;; r is temp reg used +;; Macro currently not used, save for a rainy day. + +.macro DELAY x, r + clear.d \r + addq (\x),\r ; addq zero-extends its argument +7: bne 7b + subq 1, \r +.endm + +;;---------------------------------------------------------------------------- copy_nand_to_ram: ;; copy_nand_to_ram @@ -88,7 +122,6 @@ ;; r10 - destination ;; r11 - source offset ;; r12 - size - ;; r13 - Address to jump to after completion ;; Note : r10-r12 are clobbered on return ;; Registers used: ;; r0 - NAND_RD_ADDR @@ -96,83 +129,99 @@ ;; r2 - reg_gio_rw_pa_dout ;; r3 - reg_gio_r_pa_din ;; r4 - tmp - ;; r5 - byte counter within a page - ;; r6 - reg_pinmux_rw_pa - ;; r7 - reg_gio_rw_pa_oe - ;; r8 - reg_bif_core_rw_grp3_cfg + ;; r5 - byte counter within a page / tmp2 + ;; r6 - r_bootsel masked w/ 0x18 + ;; r7 - n/u + ;; r8 - n/u ;; r9 - reg_gio_rw_pa_dout shadow - move.d 0x90000000, $r0 - move.d 0x94000000, $r1 + move.d NAND_RD_ADDR, $r0 + move.d NAND_WR_ADDR, $r1 move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r2 move.d REG_ADDR(gio, regi_gio, r_pa_din), $r3 - move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r6 - move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r7 - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r8 -#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 +#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2 lsrq 1, $r11 #endif + +#if INTERFACE_SETUP + ;; Set up pinmux + move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r5 + move.d [$r5], $r4 + or.b 0xf0, $r4 ; bits 4,5,6,7 + move.d $r4, [$r5] + ;; Set up GPIO - move.d [$r2], $r9 - move.d [$r7], $r4 + move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r5 + move.d [$r5], $r4 or.b (1<3, 08=>4, 18=>5 cycles) 1: ;; Copy one page CLR CE SET CLE + GPIO_SYNC moveq READ_CMD, $r4 move.b $r4, [$r1] - moveq 20, $r4 -2: bne 2b - subq 1, $r4 + BUS_SYNC $r4 CLR CLE SET ALE - clear.w [$r1] ; Column address = 0 - move.d $r11, $r4 + GPIO_SYNC + clear.b [$r1] ; Column address = 0 + move.d $r11, $r4 ; Address + lsrq 9, $r4 ; Row address is A9 and up + move.b $r4, [$r1] ; Row address byte #0 lsrq 8, $r4 - move.b $r4, [$r1] ; Row address + cmpq 0x08, $r6 ; 8 => Z, 0 => C, 18h => NZ,NC + bcs 4f ; C (3 cycles) => jump + move.b $r4, [$r1] ; Row address byte #1 (DELAY SLOT) (no flagset) + beq 3f ; Z (4 cycles) => jump + lsrq 8, $r4 ; (DELAY SLOT) + move.b $r4, [$r1] ; Row address byte #2 (5 cyc only) lsrq 8, $r4 - move.b $r4, [$r1] ; Row adddress - moveq 20, $r4 -2: bne 2b - subq 1, $r4 +3: + move.b $r4, [$r1] ; Row address byte #3 (5 cyc) or #2 (4 cyc) +4: + BUS_SYNC $r4 CLR ALE + GPIO_SYNC + 2: move.d [$r3], $r4 and.d 1 << BY, $r4 beq 2b - movu.w PAGE_SIZE, $r5 + nop + movu.w PAGE_SIZE_ADDRESSES, $r5 2: ; Copy one byte/word -#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 +#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2 move.w [$r0], $r4 #else move.b [$r0], $r4 #endif subq 1, $r5 bne 2b -#if CONFIG_ETRAX_FLASH_BUSWIDTH==2 +#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2 move.w $r4, [$r10+] - subu.w PAGE_SIZE*2, $r12 #else move.b $r4, [$r10+] - subu.w PAGE_SIZE, $r12 #endif - bpl 1b - addu.w PAGE_SIZE, $r11 + subu.w PAGE_SIZE_BYTES, $r12 + bhi 1b + addu.w PAGE_SIZE_ADDRESSES, $r11 - ;; End of copy - jump $r13 - nop + SET CE - ;; This will warn if the code above is too large. If you consider - ;; to remove this you don't understand the bug/feature. - .org 256 - .org ERASE_BLOCK - -normal_boot: + ;; End of copy diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/spinlock.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/spinlock.S --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/spinlock.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/spinlock.S 2006-05-24 11:38:43.000000000 +0200 @@ -1,22 +1,22 @@ ;; Core of the spinlock implementation ;; -;; Copyright (C) 2004 Axis Communications AB. +;; Copyright (C) 2004 Axis Communications AB. ;; -;; Author: Mikael Starvik - +;; Author: Mikael Starvik + .global cris_spin_lock .global cris_spin_trylock .text - + cris_spin_lock: clearf p -1: test.d [$r10] +1: test.b [$r10] beq 1b clearf p ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret @@ -24,10 +24,10 @@ cris_spin_trylock: clearf p -1: move.d [$r10], $r11 +1: move.b [$r10], $r11 ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret - move.d $r11,$r10 + movu.b $r11,$r10 diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/string.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/string.c --- linux-2.6.19.2.old/arch/cris/arch-v32/lib/string.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/string.c 2003-07-02 05:00:14.000000000 +0200 @@ -48,8 +48,8 @@ register char *dst __asm__ ("r13") = pdst; register const char *src __asm__ ("r11") = psrc; register int n __asm__ ("r12") = pn; - - + + /* When src is aligned but not dst, this makes a few extra needless cycles. I believe it would take as many to check that the re-alignment was unnecessary. */ @@ -117,13 +117,13 @@ ;; Restore registers from stack \n\ movem [$sp+],$r10" - /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) /* Inputs */ : "0" (dst), "1" (src), "2" (n)); - + } /* Either we directly starts copying, using dword copying - in a loop, or we copy as much as possible with 'movem' + in a loop, or we copy as much as possible with 'movem' and then the last block (<44 bytes) is copied here. This will work since 'movem' will have updated src,dst,n. */ diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/init.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/init.c --- linux-2.6.19.2.old/arch/cris/arch-v32/mm/init.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/init.c 2006-10-13 14:43:14.000000000 +0200 @@ -34,12 +34,12 @@ unsigned long mmu_kbase_hi; unsigned long mmu_kbase_lo; unsigned short mmu_page_id; - - /* + + /* * Make sure the current pgd table points to something sane, even if it * is most probably not used until the next switch_mm. */ - per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd; + per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd; #ifdef CONFIG_SMP { @@ -65,7 +65,7 @@ REG_STATE(mmu, rw_mm_cfg, seg_d, page) | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAXFS_SIM REG_STATE(mmu, rw_mm_cfg, seg_a, page) | #else REG_STATE(mmu, rw_mm_cfg, seg_a, linear) | @@ -115,7 +115,7 @@ SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi); SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo); SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id); - + /* Update the data MMU. */ SUPP_BANK_SEL(BANK_DM); SUPP_REG_WR(RW_MM_CFG, mmu_config); @@ -125,7 +125,7 @@ SPEC_REG_WR(SPEC_REG_PID, 0); - /* + /* * The MMU has been enabled ever since head.S but just to make it * totally obvious enable it here as well. */ @@ -133,7 +133,7 @@ SUPP_REG_WR(RW_GC_CFG, 0xf); /* IMMU, DMMU, ICache, DCache on */ } -void __init +void __init paging_init(void) { int i; @@ -160,13 +160,13 @@ for (i = 1; i < MAX_NR_ZONES; i++) zones_size[i] = 0; - /* + /* * Use free_area_init_node instead of free_area_init, because it is - * designed for systems where the DRAM starts at an address + * designed for systems where the DRAM starts at an address * substantially higher than 0, like us (we start at PAGE_OFFSET). This * saves space in the mem_map page array. */ free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); - + mem_map = contig_page_data.node_mem_map; } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/intmem.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/intmem.c --- linux-2.6.19.2.old/arch/cris/arch-v32/mm/intmem.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/intmem.c 2006-01-02 12:27:04.000000000 +0100 @@ -27,7 +27,7 @@ { static int initiated = 0; if (!initiated) { - struct intmem_allocation* alloc = + struct intmem_allocation* alloc = (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL); INIT_LIST_HEAD(&intmem_allocations); intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE); @@ -44,7 +44,7 @@ struct intmem_allocation* allocation; struct intmem_allocation* tmp; void* ret = NULL; - + preempt_disable(); crisv32_intmem_init(); @@ -55,7 +55,7 @@ if (allocation->status == STATUS_FREE && allocation->size >= size + alignment) { if (allocation->size > size + alignment) { - struct intmem_allocation* alloc = + struct intmem_allocation* alloc = (struct intmem_allocation*) kmalloc(sizeof *alloc, GFP_ATOMIC); alloc->status = STATUS_FREE; @@ -73,13 +73,13 @@ allocation->offset += alignment; list_add_tail(&tmp->entry, &allocation->entry); } - } + } allocation->status = STATUS_ALLOCATED; allocation->size = size; ret = (void*)((int)intmem_virtual + allocation->offset); } } - preempt_enable(); + preempt_enable(); return ret; } @@ -96,22 +96,22 @@ list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { if (allocation->offset == (int)(addr - intmem_virtual)) { - struct intmem_allocation* prev = - list_entry(allocation->entry.prev, + struct intmem_allocation* prev = + list_entry(allocation->entry.prev, struct intmem_allocation, entry); - struct intmem_allocation* next = - list_entry(allocation->entry.next, + struct intmem_allocation* next = + list_entry(allocation->entry.next, struct intmem_allocation, entry); allocation->status = STATUS_FREE; /* Join with prev and/or next if also free */ - if (prev->status == STATUS_FREE) { + if ((prev != &intmem_allocations) && (prev->status == STATUS_FREE)) { prev->size += allocation->size; list_del(&allocation->entry); kfree(allocation); allocation = prev; } - if (next->status == STATUS_FREE) { + if ((next != &intmem_allocations) && (next->status == STATUS_FREE)) { allocation->size += next->size; list_del(&next->entry); kfree(next); @@ -125,13 +125,13 @@ void* crisv32_intmem_phys_to_virt(unsigned long addr) { - return (void*)(addr - MEM_INTMEM_START+ + return (void*)(addr - MEM_INTMEM_START+ (unsigned long)intmem_virtual); } unsigned long crisv32_intmem_virt_to_phys(void* addr) { - return (unsigned long)((unsigned long )addr - + return (unsigned long)((unsigned long )addr - (unsigned long)intmem_virtual + MEM_INTMEM_START); } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/mmu.S linux-2.6.19.2.dev/arch/cris/arch-v32/mm/mmu.S --- linux-2.6.19.2.old/arch/cris/arch-v32/mm/mmu.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/mmu.S 2006-08-04 10:10:20.000000000 +0200 @@ -1,3 +1,5 @@ +; WARNING : The refill handler has been modified, see below !!! + /* * Copyright (C) 2003 Axis Communications AB * @@ -9,7 +11,7 @@ #include #include - + ; Save all register. Must save in same order as struct pt_regs. .macro SAVE_ALL subq 12, $sp @@ -29,11 +31,11 @@ subq 14*4, $sp movem $r13, [$sp] subq 4, $sp - move.d $r10, [$sp] + move.d $r10, [$sp] .endm ; Bus fault handler. Extracts relevant information and calls mm subsystem -; to handle the fault. +; to handle the fault. .macro MMU_BUS_FAULT_HANDLER handler, mmu, we, ex .globl \handler \handler: @@ -45,7 +47,7 @@ orq \ex << 1, $r13 ; execute? move $s3, $r10 ; rw_mm_cause and.d ~8191, $r10 ; Get faulting page start address - + jsr do_page_fault nop ba ret_from_intr @@ -59,15 +61,28 @@ ; The code below handles case 1 and calls the mm subsystem for case 2 and 3. ; Do not touch this code without very good reasons and extensive testing. ; Note that the code is optimized to minimize stalls (makes the code harder -; to read). +; to read). +; +; WARNING !!! +; Modified by Mikael Asker 060725: added a workaround for strange TLB +; behavior. If the same PTE is present in more than one set, the TLB +; doesn't recognize it and we get stuck in a loop of refill exceptions. +; The workaround detects such loops and exits them by flushing +; the TLB contents. The problem and workaround were verified +; in VCS by Mikael Starvik. ; ; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each -; PMD holds 16 MB of virtual memory. +; PMD holds 16 MB of virtual memory. ; Bits 0-12 : Offset within a page ; Bits 13-23 : PTE offset within a PMD ; Bits 24-31 : PMD offset within the PGD - + .macro MMU_REFILL_HANDLER handler, mmu + .data +1: .dword 0 ; refill_count + ; == 0 <=> last_refill_cause is invalid +2: .dword 0 ; last_refill_cause + .text .globl \handler \handler: subq 4, $sp @@ -76,40 +91,88 @@ subq 4, $sp move \mmu, $srs ; Select MMU support register bank move.d $acr, [$sp] - subq 4, $sp - move.d $r0, [$sp] -#ifdef CONFIG_SMP + subq 12, $sp + move.d 1b, $acr ; Point to refill_count + movem $r2, [$sp] + + test.d [$acr] ; refill_count == 0 ? + beq 5f ; yes, last_refill_cause is invalid + move.d $acr, $r1 + + ; last_refill_cause is valid, investigate cause + addq 4, $r1 ; Point to last_refill_cause + move $s3, $r0 ; Get rw_mm_cause + move.d [$r1], $r2 ; Get last_refill_cause + cmp.d $r0, $r2 ; rw_mm_cause == last_refill_cause ? + beq 6f ; yes, increment count + moveq 1, $r2 + + ; rw_mm_cause != last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move.d $r0, [$r1] ; last_refill_cause = rw_mm_cause + +3: ; Probably not in a loop, continue normal processing +#ifdef CONFIG_SMP move $s7, $acr ; PGD #else move.d per_cpu__current_pgd, $acr ; PGD #endif ; Look up PMD in PGD - move $s3, $r0 ; rw_mm_cause lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31) move.d [$acr], $acr ; PGD for the current process addi $r0.d, $acr, $acr move $s3, $r0 ; rw_mm_cause move.d [$acr], $acr ; Get PMD - beq 1f + beq 8f ; Look up PTE in PMD lsrq PAGE_SHIFT, $r0 and.w PAGE_MASK, $acr ; Remove PMD flags and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23) addi $r0.d, $acr, $acr move.d [$acr], $acr ; Get PTE - beq 2f - move.d [$sp+], $r0 ; Pop r0 in delayslot + beq 9f + movem [$sp], $r2 ; Restore r0-r2 in delay slot + addq 12, $sp ; Store in TLB move $acr, $s5 - ; Return +4: ; Return move.d [$sp+], $acr move [$sp], $srs addq 4, $sp rete rfe -1: ; PMD missing, let the mm subsystem fix it up. - move.d [$sp+], $r0 ; Pop r0 -2: ; PTE missing, let the mm subsystem fix it up. + +5: ; last_refill_cause is invalid + moveq 1, $r2 + addq 4, $r1 ; Point to last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move $s3, $r0 ; Get rw_mm_cause + ba 3b ; Continue normal processing + move.d $r0,[$r1] ; last_refill_cause = rw_mm_cause + +6: ; rw_mm_cause == last_refill_cause + move.d [$acr], $r2 ; Get refill_count + cmpq 4, $r2 ; refill_count > 4 ? + bhi 7f ; yes + addq 1, $r2 ; refill_count++ + ba 3b ; Continue normal processing + move.d $r2, [$acr] + +7: ; refill_count > 4, error + subq 4, $sp + move $srp, [$sp] + jsr __flush_tlb_all + move.d $acr, $r0 ; Save pointer to refill_count + move [$sp+], $srp + clear.d [$r0] ; refill_count = 0 + movem [$sp], $r2 + ba 4b ; Return + addq 12, $sp + +8: ; PMD missing, let the mm subsystem fix it up. + movem [$sp], $r2 ; Restore r0-r2 +9: ; PTE missing, let the mm subsystem fix it up. + addq 12, $sp move.d [$sp+], $acr move [$sp], $srs addq 4, $sp @@ -128,7 +191,7 @@ ba ret_from_intr nop .endm - + ; This is the MMU bus fault handlers. MMU_REFILL_HANDLER i_mmu_refill, 1 diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/tlb.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/tlb.c --- linux-2.6.19.2.old/arch/cris/arch-v32/mm/tlb.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/tlb.c 2006-08-07 12:06:44.000000000 +0200 @@ -2,7 +2,7 @@ * Low level TLB handling. * * Copyright (C) 2000-2003, Axis Communications AB. - * + * * Authors: Bjorn Wesen * Tobias Anderberg , CRISv32 port. */ @@ -79,7 +79,7 @@ void __flush_tlb_mm(struct mm_struct *mm) { - int i; + int i; int mmu; unsigned long flags; unsigned long page_id; @@ -90,7 +90,7 @@ if (page_id == NO_CONTEXT) return; - + /* Mark the TLB entries that match the page_id as invalid. */ local_save_flags(flags); local_irq_disable(); @@ -99,15 +99,15 @@ SUPP_BANK_SEL(mmu); for (i = 0; i < NUM_TLB_ENTRIES; i++) { UPDATE_TLB_SEL_IDX(i); - + /* Get the page_id */ SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi); /* Check if the page_id match. */ if ((tlb_hi & 0xff) == page_id) { mmu_tlb_hi = (REG_FIELD(mmu, rw_mm_tlb_hi, pid, - INVALID_PAGEID) - | REG_FIELD(mmu, rw_mm_tlb_hi, vpn, + INVALID_PAGEID) + | REG_FIELD(mmu, rw_mm_tlb_hi, vpn, i & 0xf)); UPDATE_TLB_HILO(mmu_tlb_hi, 0); @@ -135,7 +135,7 @@ return; addr &= PAGE_MASK; - + /* * Invalidate those TLB entries that match both the mm context and the * requested virtual address. @@ -150,11 +150,11 @@ SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi); /* Check if page_id and address matches */ - if (((tlb_hi & 0xff) == page_id) && + if (((tlb_hi & 0xff) == page_id) && ((tlb_hi & PAGE_MASK) == addr)) { mmu_tlb_hi = REG_FIELD(mmu, rw_mm_tlb_hi, pid, INVALID_PAGEID) | addr; - + UPDATE_TLB_HILO(mmu_tlb_hi, 0); } } @@ -178,33 +178,35 @@ static DEFINE_SPINLOCK(mmu_context_lock); /* Called in schedule() just before actually doing the switch_to. */ -void +void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) -{ - int cpu = smp_processor_id(); - - /* Make sure there is a MMU context. */ - spin_lock(&mmu_context_lock); - get_mmu_context(next); - cpu_set(cpu, next->cpu_vm_mask); - spin_unlock(&mmu_context_lock); - - /* - * Remember the pgd for the fault handlers. Keep a seperate copy of it - * because current and active_mm might be invalid at points where - * there's still a need to derefer the pgd. - */ - per_cpu(current_pgd, cpu) = next->pgd; - - /* Switch context in the MMU. */ - if (tsk && task_thread_info(tsk)) - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls); - } - else - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); - } +{ + if (prev != next) { + int cpu = smp_processor_id(); + + /* Make sure there is a MMU context. */ + spin_lock(&mmu_context_lock); + get_mmu_context(next); + cpu_set(cpu, next->cpu_vm_mask); + spin_unlock(&mmu_context_lock); + + /* + * Remember the pgd for the fault handlers. Keep a seperate copy of it + * because current and active_mm might be invalid at points where + * there's still a need to derefer the pgd. + */ + per_cpu(current_pgd, cpu) = next->pgd; + + /* Switch context in the MMU. */ + if (tsk && task_thread_info(tsk)) + { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls); + } + else + { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); + } + } } diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/vmlinux.lds.S linux-2.6.19.2.dev/arch/cris/arch-v32/vmlinux.lds.S --- linux-2.6.19.2.old/arch/cris/arch-v32/vmlinux.lds.S 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/arch-v32/vmlinux.lds.S 2006-10-13 14:43:11.000000000 +0200 @@ -5,11 +5,11 @@ * script. It is for example quite vital that all generated sections * that are used are actually named here, otherwise the linker will * put them at the end, where the init stuff is which is FREED after - * the kernel has booted. - */ + * the kernel has booted. + */ #include - + jiffies = jiffies_64; SECTIONS { @@ -20,7 +20,7 @@ /* The boot section is only necessary until the VCS top level testbench */ /* includes both flash and DRAM. */ .boot : { *(.boot) } - + . = DRAM_VIRTUAL_BASE + 0x4000; /* See head.S and pages reserved at the start. */ _text = .; /* Text and read-only data. */ @@ -35,7 +35,7 @@ *(.text.__*) } - _etext = . ; /* End of text section. */ + _etext = . ; /* End of text section. */ __etext = .; . = ALIGN(4); /* Exception table. */ @@ -59,7 +59,7 @@ . = ALIGN(8192); /* Init code and data. */ __init_begin = .; - .init.text : { + .init.text : { _sinittext = .; *(.init.text) _einittext = .; @@ -81,7 +81,7 @@ *(.initcall5.init); *(.initcall6.init); *(.initcall7.init); - __initcall_end = .; + __initcall_end = .; } .con_initcall.init : { @@ -94,20 +94,20 @@ __per_cpu_start = .; .data.percpu : { *(.data.percpu) } __per_cpu_end = .; - + .init.ramfs : { __initramfs_start = .; *(.init.ramfs) __initramfs_end = .; - /* + /* * We fill to the next page, so we can discard all init * pages without needing to consider what payload might be * appended to the kernel image. */ - FILL (0); + FILL (0); . = ALIGN (8192); } - + __vmlinux_end = .; /* Last address of the physical file. */ __init_end = .; diff -urN linux-2.6.19.2.old/arch/cris/kernel/crisksyms.c linux-2.6.19.2.dev/arch/cris/kernel/crisksyms.c --- linux-2.6.19.2.old/arch/cris/kernel/crisksyms.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/crisksyms.c 2006-11-03 13:49:17.000000000 +0100 @@ -9,7 +9,7 @@ #include #include #include - + #include #include #include @@ -28,6 +28,7 @@ extern void __ashldi3(void); extern void __ashrdi3(void); extern void __lshrdi3(void); +extern void __negdi2(void); extern void iounmap(volatile void * __iomem); /* Platform dependent support */ @@ -35,18 +36,7 @@ EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL(loops_per_usec); -/* String functions */ -EXPORT_SYMBOL(memcmp); -EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strcpy); -EXPORT_SYMBOL(strchr); -EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strcat); -EXPORT_SYMBOL(strncat); -EXPORT_SYMBOL(strncmp); -EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(ktime_get_ts); /* Math functions */ EXPORT_SYMBOL(__Udiv); @@ -56,6 +46,7 @@ EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__ashrdi3); EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__negdi2); /* Memory functions */ EXPORT_SYMBOL(__ioremap); @@ -85,4 +76,4 @@ EXPORT_SYMBOL(del_fast_timer); EXPORT_SYMBOL(schedule_usleep); #endif - +EXPORT_SYMBOL(csum_partial); diff -urN linux-2.6.19.2.old/arch/cris/kernel/irq.c linux-2.6.19.2.dev/arch/cris/kernel/irq.c --- linux-2.6.19.2.old/arch/cris/kernel/irq.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/irq.c 2007-01-09 10:29:20.000000000 +0100 @@ -92,14 +92,16 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { unsigned long sp; + struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); sp = rdsp(); if (unlikely((sp & (PAGE_SIZE - 1)) < (PAGE_SIZE/8))) { printk("do_IRQ: stack overflow: %lX\n", sp); show_stack(NULL, (unsigned long *)sp); } - __do_IRQ(irq, regs); + __do_IRQ(irq); irq_exit(); + set_irq_regs(old_regs); } void weird_irq(void) diff -urN linux-2.6.19.2.old/arch/cris/kernel/process.c linux-2.6.19.2.dev/arch/cris/kernel/process.c --- linux-2.6.19.2.old/arch/cris/kernel/process.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/process.c 2006-06-25 17:00:10.000000000 +0200 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.21 2005/03/04 08:16:17 starvik Exp $ +/* $Id: process.c,v 1.26 2006/06/25 15:00:10 starvik Exp $ * * linux/arch/cris/kernel/process.c * @@ -8,6 +8,21 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: process.c,v $ + * Revision 1.26 2006/06/25 15:00:10 starvik + * Merge of Linux 2.6.17 + * + * Revision 1.25 2006/03/22 09:56:56 starvik + * Merge of Linux 2.6.16 + * + * Revision 1.24 2006/01/04 06:09:48 starvik + * Merge of Linux 2.6.15 + * + * Revision 1.23 2005/08/29 07:32:19 starvik + * Merge of 2.6.13 + * + * Revision 1.22 2005/08/18 08:33:18 starvik + * Corrected signature of machine_restart + * * Revision 1.21 2005/03/04 08:16:17 starvik * Merge of Linux 2.6.11. * @@ -195,12 +210,18 @@ */ void (*pm_idle)(void); +extern void default_idle(void); + +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + /* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a * low exit latency (ie sit in a loop waiting for * somebody to say that they'd like to reschedule) */ + void cpu_idle (void) { /* endless idle loop with no priority at all */ diff -urN linux-2.6.19.2.old/arch/cris/kernel/profile.c linux-2.6.19.2.dev/arch/cris/kernel/profile.c --- linux-2.6.19.2.old/arch/cris/kernel/profile.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/profile.c 2004-10-05 08:22:44.000000000 +0200 @@ -42,7 +42,7 @@ return count; } -static ssize_t +static ssize_t write_cris_profile(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { diff -urN linux-2.6.19.2.old/arch/cris/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/kernel/ptrace.c --- linux-2.6.19.2.old/arch/cris/kernel/ptrace.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/ptrace.c 2006-03-23 15:54:02.000000000 +0100 @@ -8,6 +8,9 @@ * Authors: Bjorn Wesen * * $Log: ptrace.c,v $ + * Revision 1.11 2006/03/23 14:54:02 starvik + * Corrected signal handling. + * * Revision 1.10 2004/09/22 11:50:01 orjanf * * Moved get_reg/put_reg to arch-specific files. * * Added functions to access debug registers (CRISv32). @@ -82,13 +85,13 @@ /* notification of userspace execution resumption * - triggered by current->work.notify_resume */ -extern int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); +extern int do_signal(int canrestart, struct pt_regs *regs); -void do_notify_resume(int canrestart, sigset_t *oldset, struct pt_regs *regs, +void do_notify_resume(int canrestart, struct pt_regs *regs, __u32 thread_info_flags ) { /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) - do_signal(canrestart,oldset,regs); + do_signal(canrestart,regs); } diff -urN linux-2.6.19.2.old/arch/cris/kernel/semaphore.c linux-2.6.19.2.dev/arch/cris/kernel/semaphore.c --- linux-2.6.19.2.old/arch/cris/kernel/semaphore.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/semaphore.c 2005-10-31 09:48:05.000000000 +0100 @@ -4,7 +4,7 @@ */ #include -#include +#include #include /* @@ -95,6 +95,7 @@ tsk->state = TASK_RUNNING; \ remove_wait_queue(&sem->wait, &wait); + void __sched __down(struct semaphore * sem) { DOWN_VAR diff -urN linux-2.6.19.2.old/arch/cris/kernel/setup.c linux-2.6.19.2.dev/arch/cris/kernel/setup.c --- linux-2.6.19.2.old/arch/cris/kernel/setup.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/setup.c 2007-01-09 10:29:20.000000000 +0100 @@ -18,7 +18,7 @@ #include #include #include - +#include #include /* @@ -36,6 +36,8 @@ extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ +static struct cpu cpu_devices[NR_CPUS]; + extern void show_etrax_copyright(void); /* arch-vX/kernel/setup.c */ /* This mainly sets up the memory area, and can be really confusing. @@ -187,4 +189,14 @@ .show = show_cpuinfo, }; +static int __init topology_init(void) +{ + int i; + + for_each_possible_cpu(i) { + return register_cpu(&cpu_devices[i], i); + } +} + +subsys_initcall(topology_init); diff -urN linux-2.6.19.2.old/arch/cris/kernel/sys_cris.c linux-2.6.19.2.dev/arch/cris/kernel/sys_cris.c --- linux-2.6.19.2.old/arch/cris/kernel/sys_cris.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/sys_cris.c 2007-01-09 10:29:20.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: sys_cris.c,v 1.6 2004/03/11 11:38:40 starvik Exp $ +/* $Id: sys_cris.c,v 1.7 2007/01/09 09:29:20 starvik Exp $ * * linux/arch/cris/kernel/sys_cris.c * @@ -172,3 +172,4 @@ return -ENOSYS; } } + diff -urN linux-2.6.19.2.old/arch/cris/kernel/time.c linux-2.6.19.2.dev/arch/cris/kernel/time.c --- linux-2.6.19.2.old/arch/cris/kernel/time.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/time.c 2007-01-09 10:29:20.000000000 +0100 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $ +/* $Id: time.c,v 1.23 2007/01/09 09:29:20 starvik Exp $ * * linux/arch/cris/kernel/time.c * @@ -172,10 +172,6 @@ mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); - printk(KERN_DEBUG - "rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", - sec, min, hour, day, mon, year); - BCD_TO_BIN(sec); BCD_TO_BIN(min); BCD_TO_BIN(hour); @@ -208,11 +204,11 @@ cris_do_profile(struct pt_regs* regs) { -#if CONFIG_SYSTEM_PROFILER +#ifdef CONFIG_SYSTEM_PROFILER cris_profile_sample(regs); #endif -#if CONFIG_PROFILING +#ifdef CONFIG_PROFILING profile_tick(CPU_PROFILING, regs); #endif } @@ -222,10 +218,15 @@ */ unsigned long long sched_clock(void) { - return (unsigned long long)jiffies * (1000000000 / HZ); + unsigned long long ns; + + ns = jiffies; + ns *= 1000000000 / HZ; + ns += get_ns_in_jiffie(); + return ns; } -static int +static int __init init_udelay(void) { loops_per_usec = (loops_per_jiffy * HZ) / 1000000; diff -urN linux-2.6.19.2.old/arch/cris/kernel/traps.c linux-2.6.19.2.dev/arch/cris/kernel/traps.c --- linux-2.6.19.2.old/arch/cris/kernel/traps.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/kernel/traps.c 2006-12-11 14:04:23.000000000 +0100 @@ -1,66 +1,78 @@ -/* $Id: traps.c,v 1.11 2005/01/24 16:03:19 orjanf Exp $ - * +/* * linux/arch/cris/traps.c * - * Here we handle the break vectors not used by the system call - * mechanism, as well as some general stack/register dumping + * Here we handle the break vectors not used by the system call + * mechanism, as well as some general stack/register dumping * things. - * - * Copyright (C) 2000-2002 Axis Communications AB + * + * Copyright (C) 2000-2006 Axis Communications AB * * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Hans-Peter Nilsson * */ #include #include + #include #include +extern void arch_enable_nmi(void); +extern void stop_watchdog(void); +extern void reset_watchdog(void); +extern void show_registers(struct pt_regs *regs); + +#ifdef CONFIG_DEBUG_BUGVERBOSE +extern void handle_BUG(struct pt_regs *regs); +#else +#define handle_BUG(regs) +#endif + static int kstack_depth_to_print = 24; -extern int raw_printk(const char *fmt, ...); +void (*nmi_handler)(struct pt_regs*); -void show_trace(unsigned long * stack) +void +show_trace(unsigned long *stack) { unsigned long addr, module_start, module_end; extern char _stext, _etext; int i; - raw_printk("\nCall Trace: "); + printk("\nCall Trace: "); - i = 1; - module_start = VMALLOC_START; - module_end = VMALLOC_END; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; - while (((long) stack & (THREAD_SIZE-1)) != 0) { - if (__get_user (addr, stack)) { + while (((long)stack & (THREAD_SIZE-1)) != 0) { + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (((addr >= (unsigned long) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - raw_printk("\n "); - raw_printk("[<%08lx>] ", addr); - i++; - } - } + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long)&_stext) && + (addr <= (unsigned long)&_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } } /* @@ -78,109 +90,150 @@ * with the ksymoops maintainer. */ -void +void show_stack(struct task_struct *task, unsigned long *sp) { - unsigned long *stack, addr; - int i; + unsigned long *stack, addr; + int i; /* * debugging aid: "show_stack(NULL);" prints a * back trace. */ - if(sp == NULL) { + if (sp == NULL) { if (task) sp = (unsigned long*)task->thread.ksp; else sp = (unsigned long*)rdsp(); } - stack = sp; + stack = sp; - raw_printk("\nStack from %08lx:\n ", (unsigned long)stack); - for(i = 0; i < kstack_depth_to_print; i++) { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - if (i && ((i % 8) == 0)) - raw_printk("\n "); - if (__get_user (addr, stack)) { + printk("\nStack from %08lx:\n ", (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long)stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - raw_printk("%08lx ", addr); - } + printk("%08lx ", addr); + } show_trace(sp); } -static void (*nmi_handler)(struct pt_regs*); -extern void arch_enable_nmi(void); +#if 0 +/* displays a short stack trace */ -void set_nmi_handler(void (*handler)(struct pt_regs*)) +int +show_stack(void) { - nmi_handler = handler; - arch_enable_nmi(); + unsigned long *sp = (unsigned long *)rdusp(); + int i; + + printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + for (i = 0; i < 16; i++) + printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + return 0; } +#endif -void handle_nmi(struct pt_regs* regs) +void +dump_stack(void) { - if (nmi_handler) - nmi_handler(regs); + show_stack(NULL, NULL); +} + +EXPORT_SYMBOL(dump_stack); + +void +set_nmi_handler(void (*handler)(struct pt_regs*)) +{ + nmi_handler = handler; + arch_enable_nmi(); } #ifdef CONFIG_DEBUG_NMI_OOPS -void oops_nmi_handler(struct pt_regs* regs) +void +oops_nmi_handler(struct pt_regs* regs) { - stop_watchdog(); - raw_printk("NMI!\n"); - show_registers(regs); + stop_watchdog(); + oops_in_progress = 1; + printk("NMI!\n"); + show_registers(regs); + oops_in_progress = 0; } -static int -__init oops_nmi_register(void) +static int __init +oops_nmi_register(void) { - set_nmi_handler(oops_nmi_handler); - return 0; + set_nmi_handler(oops_nmi_handler); + return 0; } __initcall(oops_nmi_register); #endif -#if 0 -/* displays a short stack trace */ - -int -show_stack() +/* + * This gets called from entry.S when the watchdog has bitten. Show something + * similiar to an Oops dump, and if the kernel is configured to be a nice + * doggy, then halt instead of reboot. + */ +void +watchdog_bite_hook(struct pt_regs *regs) { - unsigned long *sp = (unsigned long *)rdusp(); - int i; - raw_printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); - for(i = 0; i < 16; i++) - raw_printk("sp + %d: 0x%08lx\n", i*4, sp[i]); - return 0; -} +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + + while (1) + ; /* Do nothing. */ +#else + show_registers(regs); #endif +} -void dump_stack(void) +/* This is normally the Oops function. */ +void +die_if_kernel(const char *str, struct pt_regs *regs, long err) { - show_stack(NULL, NULL); -} + if (user_mode(regs)) + return; -EXPORT_SYMBOL(dump_stack); +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* + * This printout might take too long and could trigger + * the watchdog normally. If NICE_DOGGY is set, simply + * stop the watchdog during the printout. + */ + stop_watchdog(); +#endif -void __init -trap_init(void) -{ - /* Nothing needs to be done */ + handle_BUG(regs); + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + + oops_in_progress = 0; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + do_exit(SIGSEGV); } -void spinning_cpu(void* addr) +void __init +trap_init(void) { - raw_printk("CPU %d spinning on %X\n", smp_processor_id(), addr); - dump_stack(); + /* Nothing needs to be done */ } diff -urN linux-2.6.19.2.old/arch/cris/mm/fault.c linux-2.6.19.2.dev/arch/cris/mm/fault.c --- linux-2.6.19.2.old/arch/cris/mm/fault.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/mm/fault.c 2006-09-29 13:14:06.000000000 +0200 @@ -1,11 +1,27 @@ /* * linux/arch/cris/mm/fault.c * - * Copyright (C) 2000, 2001 Axis Communications AB + * Copyright (C) 2000-2006 Axis Communications AB + * + * Authors: Bjorn Wesen * - * Authors: Bjorn Wesen - * * $Log: fault.c,v $ + * Revision 1.25 2006/09/29 11:14:06 orjanf + * * Use arch-independent macro to get irp/erp for v10/v32. + * + * Revision 1.24 2006/09/29 10:58:09 orjanf + * * Added user mode SIGSEGV printk. + * + * Revision 1.23 2006/06/20 07:42:56 pkj + * Removed an unnecessary reference to raw_printk(). + * + * Revision 1.22 2005/08/29 07:32:20 starvik + * Merge of 2.6.13 + * + * Revision 1.21 2005/07/02 12:29:37 starvik + * Use the generic oops_in_progress instead of the raw_printk hack. + * Moved some functions to achr-independent code. + * * Revision 1.20 2005/03/04 08:16:18 starvik * Merge of Linux 2.6.11. * @@ -135,7 +151,6 @@ extern int find_fixup_code(struct pt_regs *); extern void die_if_kernel(const char *, struct pt_regs *, long); -extern int raw_printk(const char *fmt, ...); /* debug of low-level TLB reload */ #undef DEBUG @@ -164,8 +179,8 @@ * address. * * error_code: - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write * * If this routine detects a bad access, it returns 1, otherwise it * returns 0. @@ -180,9 +195,9 @@ struct vm_area_struct * vma; siginfo_t info; - D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", - address, smp_processor_id(), instruction_pointer(regs), - protection, writeaccess)); + D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", + address, smp_processor_id(), instruction_pointer(regs), + protection, writeaccess)); tsk = current; @@ -318,6 +333,8 @@ /* info.si_code has been set above */ info.si_addr = (void *)address; force_sig_info(SIGSEGV, &info, tsk); + printk(KERN_NOTICE "%s (pid %d) segfaults for page address %08lx at pc %08lx\n", + tsk->comm, tsk->pid, address, instruction_pointer(regs)); return; } @@ -325,7 +342,7 @@ /* Are we prepared to handle this kernel fault? * - * (The kernel has valid exception-points in the source + * (The kernel has valid exception-points in the source * when it acesses user-memory. When it fails in one * of those points, we find it in a table and do a jump * to some fixup code that loads an appropriate error @@ -340,13 +357,17 @@ * terminate things with extreme prejudice. */ - if ((unsigned long) (address) < PAGE_SIZE) - raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - raw_printk(KERN_ALERT "Unable to handle kernel access"); - raw_printk(" at virtual address %08lx\n",address); + if (!oops_in_progress) { + oops_in_progress = 1; + if ((unsigned long) (address) < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access"); + printk(" at virtual address %08lx\n",address); - die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + oops_in_progress = 0; + } do_exit(SIGKILL); @@ -405,8 +426,8 @@ /* Since we're two-level, we don't need to do both * set_pgd and set_pmd (they do the same thing). If * we go three-level at some point, do the right thing - * with pgd_present and set_pgd here. - * + * with pgd_present and set_pgd here. + * * Also, since the vmalloc area is global, we don't * need to copy individual PTE's, it is enough to * copy the pgd pointer into the pte page of the diff -urN linux-2.6.19.2.old/arch/cris/mm/init.c linux-2.6.19.2.dev/arch/cris/mm/init.c --- linux-2.6.19.2.old/arch/cris/mm/init.c 2007-01-10 20:10:37.000000000 +0100 +++ linux-2.6.19.2.dev/arch/cris/mm/init.c 2006-06-25 17:00:10.000000000 +0200 @@ -7,6 +7,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: init.c,v $ + * Revision 1.14 2006/06/25 15:00:10 starvik + * Merge of Linux 2.6.17 + * + * Revision 1.13 2005/06/20 05:30:00 starvik + * Remove unnecessary diff to kernel.org tree + * + * Revision 1.12 2004/08/16 12:37:24 starvik + * Merge of Linux 2.6.8 + * * Revision 1.11 2004/05/28 09:28:56 starvik * Calculation of loops_per_usec moved because initalization order has changed * in Linux 2.6.