]> nv-tegra.nvidia Code Review - linux-3.10.git/commitdiff
[PATCH] CRIS update: new subarchitecture v32
authorMikael Starvik <mikael.starvik@axis.com>
Wed, 27 Jul 2005 18:44:44 +0000 (11:44 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 27 Jul 2005 23:26:01 +0000 (16:26 -0700)
New CRIS sub architecture named v32.

From: Dave Jones <davej@redhat.com>

Fix swapped kmalloc args

Signed-off-by: Mikael Starvik <starvik@axis.com>
Signed-off-by: Dave Jones <davej@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
193 files changed:
arch/cris/arch-v32/Kconfig [new file with mode: 0644]
arch/cris/arch-v32/boot/Makefile [new file with mode: 0644]
arch/cris/arch-v32/boot/compressed/Makefile [new file with mode: 0644]
arch/cris/arch-v32/boot/compressed/README [new file with mode: 0644]
arch/cris/arch-v32/boot/compressed/decompress.ld [new file with mode: 0644]
arch/cris/arch-v32/boot/compressed/head.S [new file with mode: 0644]
arch/cris/arch-v32/boot/compressed/misc.c [new file with mode: 0644]
arch/cris/arch-v32/boot/rescue/Makefile [new file with mode: 0644]
arch/cris/arch-v32/boot/rescue/head.S [new file with mode: 0644]
arch/cris/arch-v32/boot/rescue/rescue.ld [new file with mode: 0644]
arch/cris/arch-v32/drivers/Kconfig [new file with mode: 0644]
arch/cris/arch-v32/drivers/Makefile [new file with mode: 0644]
arch/cris/arch-v32/drivers/axisflashmap.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/cryptocop.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/gpio.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/i2c.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/i2c.h [new file with mode: 0644]
arch/cris/arch-v32/drivers/iop_fw_load.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/nandflash.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/pcf8563.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/pci/Makefile [new file with mode: 0644]
arch/cris/arch-v32/drivers/pci/bios.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/pci/dma.c [new file with mode: 0644]
arch/cris/arch-v32/drivers/sync_serial.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/Makefile [new file with mode: 0644]
arch/cris/arch-v32/kernel/arbiter.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/asm-offsets.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/crisksyms.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/debugport.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/dma.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/entry.S [new file with mode: 0644]
arch/cris/arch-v32/kernel/fasttimer.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/head.S [new file with mode: 0644]
arch/cris/arch-v32/kernel/io.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/irq.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/kgdb.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/kgdb_asm.S [new file with mode: 0644]
arch/cris/arch-v32/kernel/pinmux.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/process.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/ptrace.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/setup.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/signal.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/smp.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/time.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/traps.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/vcs_hook.c [new file with mode: 0644]
arch/cris/arch-v32/kernel/vcs_hook.h [new file with mode: 0644]
arch/cris/arch-v32/lib/Makefile [new file with mode: 0644]
arch/cris/arch-v32/lib/checksum.S [new file with mode: 0644]
arch/cris/arch-v32/lib/checksumcopy.S [new file with mode: 0644]
arch/cris/arch-v32/lib/csumcpfruser.S [new file with mode: 0644]
arch/cris/arch-v32/lib/dram_init.S [new file with mode: 0644]
arch/cris/arch-v32/lib/hw_settings.S [new file with mode: 0644]
arch/cris/arch-v32/lib/memset.c [new file with mode: 0644]
arch/cris/arch-v32/lib/nand_init.S [new file with mode: 0644]
arch/cris/arch-v32/lib/spinlock.S [new file with mode: 0644]
arch/cris/arch-v32/lib/string.c [new file with mode: 0644]
arch/cris/arch-v32/lib/usercopy.c [new file with mode: 0644]
arch/cris/arch-v32/mm/Makefile [new file with mode: 0644]
arch/cris/arch-v32/mm/init.c [new file with mode: 0644]
arch/cris/arch-v32/mm/intmem.c [new file with mode: 0644]
arch/cris/arch-v32/mm/mmu.S [new file with mode: 0644]
arch/cris/arch-v32/mm/tlb.c [new file with mode: 0644]
arch/cris/arch-v32/output_arch.ld [new file with mode: 0644]
arch/cris/arch-v32/vmlinux.lds.S [new file with mode: 0644]
include/asm-cris/arch-v32/arbiter.h [new file with mode: 0644]
include/asm-cris/arch-v32/atomic.h [new file with mode: 0644]
include/asm-cris/arch-v32/bitops.h [new file with mode: 0644]
include/asm-cris/arch-v32/byteorder.h [new file with mode: 0644]
include/asm-cris/arch-v32/cache.h [new file with mode: 0644]
include/asm-cris/arch-v32/checksum.h [new file with mode: 0644]
include/asm-cris/arch-v32/cryptocop.h [new file with mode: 0644]
include/asm-cris/arch-v32/delay.h [new file with mode: 0644]
include/asm-cris/arch-v32/dma.h [new file with mode: 0644]
include/asm-cris/arch-v32/elf.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/Makefile [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/ata_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/bif_core_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/bif_dma_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/bif_slave_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/config_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/cpu_vect.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/cris_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/cris_supp_reg.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/dma_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/eth_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/gio_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/intr_vect.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/intr_vect_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/irq_nmi_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/marb_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/mmu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/mmu_supp_reg.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/pinmux_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/reg_map_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/rt_trace_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/ser_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/sser_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/strcop_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/strmux_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/asm/timer_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/ata_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/bif_core_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/bif_dma_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/bif_slave_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/config_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/cpu_vect.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/dma.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/dma_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/eth_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/extmem_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/gio_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/intr_vect.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/intr_vect_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/Makefile [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_crc_par_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_dmc_in_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_dmc_out_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_fifo_in_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_fifo_in_extra_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_fifo_out_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_fifo_out_extra_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_mpu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_reg_space_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sap_in_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sap_out_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_scrc_in_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_scrc_out_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_spu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sw_cfg_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sw_cpu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sw_mpu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_sw_spu_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_timer_grp_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_trigger_grp_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/asm/iop_version_defs_asm.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_crc_par_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_dmc_in_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_dmc_out_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_fifo_in_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_fifo_in_extra_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_fifo_out_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_fifo_out_extra_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_mpu_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_mpu_macros.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_reg_space.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sap_in_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sap_out_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_scrc_in_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_scrc_out_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_spu_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sw_cfg_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sw_cpu_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sw_mpu_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_sw_spu_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_timer_grp_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_trigger_grp_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/iop/iop_version_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/irq_nmi_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/marb_bp_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/marb_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/pinmux_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/reg_map.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/reg_rdwr.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/rt_trace_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/ser_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/sser_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/strcop.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/strcop_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/strmux_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/supp_reg.h [new file with mode: 0644]
include/asm-cris/arch-v32/hwregs/timer_defs.h [new file with mode: 0644]
include/asm-cris/arch-v32/ide.h [new file with mode: 0644]
include/asm-cris/arch-v32/intmem.h [new file with mode: 0644]
include/asm-cris/arch-v32/io.h [new file with mode: 0644]
include/asm-cris/arch-v32/irq.h [new file with mode: 0644]
include/asm-cris/arch-v32/juliette.h [new file with mode: 0644]
include/asm-cris/arch-v32/memmap.h [new file with mode: 0644]
include/asm-cris/arch-v32/mmu.h [new file with mode: 0644]
include/asm-cris/arch-v32/offset.h [new file with mode: 0644]
include/asm-cris/arch-v32/page.h [new file with mode: 0644]
include/asm-cris/arch-v32/pgtable.h [new file with mode: 0644]
include/asm-cris/arch-v32/pinmux.h [new file with mode: 0644]
include/asm-cris/arch-v32/processor.h [new file with mode: 0644]
include/asm-cris/arch-v32/ptrace.h [new file with mode: 0644]
include/asm-cris/arch-v32/spinlock.h [new file with mode: 0644]
include/asm-cris/arch-v32/system.h [new file with mode: 0644]
include/asm-cris/arch-v32/thread_info.h [new file with mode: 0644]
include/asm-cris/arch-v32/timex.h [new file with mode: 0644]
include/asm-cris/arch-v32/tlb.h [new file with mode: 0644]
include/asm-cris/arch-v32/uaccess.h [new file with mode: 0644]
include/asm-cris/arch-v32/unistd.h [new file with mode: 0644]
include/asm-cris/arch-v32/user.h [new file with mode: 0644]

diff --git a/arch/cris/arch-v32/Kconfig b/arch/cris/arch-v32/Kconfig
new file mode 100644 (file)
index 0000000..22f0ddc
--- /dev/null
@@ -0,0 +1,296 @@
+config ETRAX_DRAM_VIRTUAL_BASE
+       hex
+       depends on ETRAX_ARCH_V32
+       default "c0000000"
+
+config ETRAX_LED1G
+       string "First green LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA3"
+       help
+         Bit to use for the first green LED (network LED).
+         Most Axis products use bit A3 here.
+
+config ETRAX_LED1R
+       string "First red LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA4"
+       help
+         Bit to use for the first red LED (network LED).
+         Most Axis products use bit A4 here.
+
+config ETRAX_LED2G
+       string "Second green LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA5"
+       help
+         Bit to use for the first green LED (status LED).
+         Most Axis products use bit A5 here.
+
+config ETRAX_LED2R
+       string "Second red LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA6"
+       help
+         Bit to use for the first red LED (network LED).
+         Most Axis products use bit A6 here.
+
+config ETRAX_LED3G
+       string "Third green LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA7"
+       help
+         Bit to use for the first green LED (drive/power LED).
+         Most Axis products use bit A7 here.
+
+config ETRAX_LED3R
+       string "Third red LED bit"
+       depends on ETRAX_ARCH_V32
+       default "PA7"
+       help
+         Bit to use for the first red LED (drive/power LED).
+         Most Axis products use bit A7 here.
+
+choice
+       prompt "Product debug-port"
+       depends on ETRAX_ARCH_V32
+       default ETRAX_DEBUG_PORT0
+
+config ETRAX_DEBUG_PORT0
+       bool "Serial-0"
+       help
+         Choose a serial port for the ETRAX debug console.  Default to
+         port 0.
+
+config ETRAX_DEBUG_PORT1
+       bool "Serial-1"
+       help
+         Use serial port 1 for the console.
+
+config ETRAX_DEBUG_PORT2
+       bool "Serial-2"
+       help
+         Use serial port 2 for the console.
+
+config ETRAX_DEBUG_PORT3
+       bool "Serial-3"
+       help
+         Use serial port 3 for the console.
+
+config ETRAX_DEBUG_PORT_NULL
+       bool "disabled"
+       help
+         Disable serial-port debugging.
+
+endchoice
+
+choice
+       prompt "Kernel GDB port"
+       depends on ETRAX_KGDB
+       default ETRAX_KGDB_PORT0
+       help
+         Choose a serial port for kernel debugging.  NOTE: This port should
+         not be enabled under Drivers for built-in interfaces (as it has its
+         own initialization code) and should not be the same as the debug port.
+
+config ETRAX_KGDB_PORT0
+       bool "Serial-0"
+       help
+         Use serial port 0 for kernel debugging.
+
+config ETRAX_KGDB_PORT1
+       bool "Serial-1"
+       help
+         Use serial port 1 for kernel debugging.
+
+config ETRAX_KGDB_PORT2
+       bool "Serial-2"
+       help
+         Use serial port 2 for kernel debugging.
+
+config ETRAX_KGDB_PORT3
+       bool "Serial-3"
+       help
+         Use serial port 3 for kernel debugging.
+
+endchoice
+
+config ETRAX_MEM_GRP1_CONFIG
+       hex "MEM_GRP1_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "4044a"
+       help
+         Waitstates for flash. The default value is suitable for the
+         standard flashes used in axis products (120 ns).
+
+config ETRAX_MEM_GRP2_CONFIG
+       hex "MEM_GRP2_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "0"
+       help
+         Waitstates for SRAM. 0 is a good choice for most Axis products.
+
+config ETRAX_MEM_GRP3_CONFIG
+       hex "MEM_GRP3_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "0"
+       help
+         Waitstates for CSP0-3. 0 is a good choice for most Axis products.
+         It may need to be changed if external devices such as extra
+         register-mapped LEDs are used.
+
+config ETRAX_MEM_GRP4_CONFIG
+       hex "MEM_GRP4_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "0"
+       help
+         Waitstates for CSP4-6. 0 is a good choice for most Axis products.
+
+config ETRAX_SDRAM_GRP0_CONFIG
+       hex "SDRAM_GRP0_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "336"
+       help
+         SDRAM configuration for group 0. The value depends on the
+         hardware configuration. The default value is suitable
+         for 32 MB organized as two 16 bits chips (e.g. Axis
+         part number 18550) connected as one 32 bit device (i.e. in
+         the same group).
+
+config ETRAX_SDRAM_GRP1_CONFIG
+       hex "SDRAM_GRP1_CONFIG"
+       depends on ETRAX_ARCH_V32
+       default "0"
+       help
+         SDRAM configuration for group 1. The defult value is 0
+         because group 1 is not used in the default configuration,
+         described in the help for SDRAM_GRP0_CONFIG.
+
+config ETRAX_SDRAM_TIMING
+       hex "SDRAM_TIMING"
+       depends on ETRAX_ARCH_V32
+       default "104a"
+       help
+         SDRAM timing parameters. The default value is ok for
+         most hardwares but large SDRAMs may require a faster
+         refresh (a.k.a 8K refresh). The default value implies
+         100MHz clock and SDR mode.
+
+config ETRAX_SDRAM_COMMAND
+       hex "SDRAM_COMMAND"
+       depends on ETRAX_ARCH_V32
+       default "0"
+       help
+         SDRAM command. Should be 0 unless you really know what
+         you are doing (may be != 0 for unusual address line
+         mappings such as in a MCM)..
+
+config ETRAX_DEF_GIO_PA_OE
+       hex "GIO_PA_OE"
+       depends on ETRAX_ARCH_V32
+       default "1c"
+       help
+         Configures the direction of general port A bits.  1 is out, 0 is in.
+         This is often totally different depending on the product used.
+         There are some guidelines though - if you know that only LED's are
+         connected to port PA, then they are usually connected to bits 2-4
+         and you can therefore use 1c.  On other boards which don't have the
+         LED's at the general ports, 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_PA_OUT
+       hex "GIO_PA_OUT"
+       depends on ETRAX_ARCH_V32
+       default "00"
+       help
+         Configures the initial data for the general port A bits.  Most
+         products should use 00 here.
+
+config ETRAX_DEF_GIO_PB_OE
+       hex "GIO_PB_OE"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the direction of general port B bits.  1 is out, 0 is in.
+         This is often totally different depending on the product used.
+         There are some guidelines though - if you know that only LED's are
+         connected to port PA, then they are usually connected to bits 2-4
+         and you can therefore use 1c.  On other boards which don't have the
+         LED's at the general ports, 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_PB_OUT
+       hex "GIO_PB_OUT"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the initial data for the general port B bits.  Most
+         products should use 00000 here.
+
+config ETRAX_DEF_GIO_PC_OE
+       hex "GIO_PC_OE"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the direction of general port C bits.  1 is out, 0 is in.
+         This is often totally different depending on the product used.
+         There are some guidelines though - if you know that only LED's are
+         connected to port PA, then they are usually connected to bits 2-4
+         and you can therefore use 1c.  On other boards which don't have the
+         LED's at the general ports, 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_PC_OUT
+       hex "GIO_PC_OUT"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the initial data for the general port C bits.  Most
+         products should use 00000 here.
+
+config ETRAX_DEF_GIO_PD_OE
+       hex "GIO_PD_OE"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the direction of general port D bits.  1 is out, 0 is in.
+         This is often totally different depending on the product used.
+         There are some guidelines though - if you know that only LED's are
+         connected to port PA, then they are usually connected to bits 2-4
+         and you can therefore use 1c.  On other boards which don't have the
+         LED's at the general ports, 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_PD_OUT
+       hex "GIO_PD_OUT"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the initial data for the general port D bits.  Most
+         products should use 00000 here.
+
+config ETRAX_DEF_GIO_PE_OE
+       hex "GIO_PE_OE"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the direction of general port E bits.  1 is out, 0 is in.
+         This is often totally different depending on the product used.
+         There are some guidelines though - if you know that only LED's are
+         connected to port PA, then they are usually connected to bits 2-4
+         and you can therefore use 1c.  On other boards which don't have the
+         LED's at the general ports, 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_PE_OUT
+       hex "GIO_PE_OUT"
+       depends on ETRAX_ARCH_V32
+       default "00000"
+       help
+         Configures the initial data for the general port E bits.  Most
+         products should use 00000 here.
diff --git a/arch/cris/arch-v32/boot/Makefile b/arch/cris/arch-v32/boot/Makefile
new file mode 100644 (file)
index 0000000..26f293a
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# arch/cris/arch-v32/boot/Makefile
+#
+target = $(target_boot_dir)
+src    = $(src_boot_dir)
+
+zImage: compressed/vmlinuz
+
+compressed/vmlinuz: $(objtree)/vmlinux
+       @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz
+
+clean:
+       rm -f zImage tools/build compressed/vmlinux.out
+       @$(MAKE) -f $(src)/compressed/Makefile clean
diff --git a/arch/cris/arch-v32/boot/compressed/Makefile b/arch/cris/arch-v32/boot/compressed/Makefile
new file mode 100644 (file)
index 0000000..9f77eda
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# lx25/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
+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)
+
+$(target)/head.o: $(src)/head.S
+       $(CC) -D__ASSEMBLY__ -c $< -o $@
+
+# gzip the kernel image
+
+piggy.img: $(SYSTEM)
+       cat $(SYSTEM) | gzip -f -9 > piggy.img
+
+clean:
+       rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS)
+
diff --git a/arch/cris/arch-v32/boot/compressed/README b/arch/cris/arch-v32/boot/compressed/README
new file mode 100644 (file)
index 0000000..e33691d
--- /dev/null
@@ -0,0 +1,25 @@
+Creation of the self-extracting compressed kernel image (vmlinuz)
+-----------------------------------------------------------------
+$Id: README,v 1.1 2003/08/21 09:37:03 johana Exp $
+
+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
+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.
+
+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
+into the file vmlinuz. It can be executed in an arbitrary place in flash.
+
+Be careful - it assumes some things about free locations in DRAM. It
+assumes the DRAM starts at 0x40000000 and that it is at least 8 MB,
+so it puts its code at 0x40700000, and initial stack at 0x40800000.
+
+-Bjorn
diff --git a/arch/cris/arch-v32/boot/compressed/decompress.ld b/arch/cris/arch-v32/boot/compressed/decompress.ld
new file mode 100644 (file)
index 0000000..3c837fe
--- /dev/null
@@ -0,0 +1,30 @@
+/*#OUTPUT_FORMAT(elf32-us-cris) */
+OUTPUT_ARCH (crisv32)
+
+MEMORY
+       {
+       dram : ORIGIN = 0x40700000,
+              LENGTH = 0x00100000
+       }
+
+SECTIONS
+{
+       .text :
+       {
+               _stext = . ;
+               *(.text)
+               *(.rodata)
+               *(.rodata.*)
+               _etext = . ;
+       } > dram
+       .data :
+       {
+               *(.data)
+               _edata = . ;
+       } > dram
+       .bss :
+       {
+               *(.bss)
+               _end = ALIGN( 0x10 ) ;
+       } > dram
+}
diff --git a/arch/cris/arch-v32/boot/compressed/head.S b/arch/cris/arch-v32/boot/compressed/head.S
new file mode 100644 (file)
index 0000000..0c55b83
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *  Code that sets up the DRAM registers, calls the
+ *  decompressor to unpack the piggybacked kernel, and jumps.
+ *
+ *  Copyright (C) 1999 - 2003, Axis Communications AB
+ */
+
+#include <linux/config.h>
+#define ASSEMBLER_MACROS_ONLY
+#include <asm/arch/hwregs/asm/reg_map_asm.h>
+#include <asm/arch/hwregs/asm/gio_defs_asm.h>
+#include <asm/arch/hwregs/asm/config_defs_asm.h>
+
+#define RAM_INIT_MAGIC 0x56902387
+#define COMMAND_LINE_MAGIC 0x87109563
+
+       ;; Exported symbols
+
+       .globl  input_data
+
+       .text
+start:
+       di
+
+       ;; Start clocks for used blocks.
+       move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
+       move.d [$r1], $r0
+       or.d   REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
+              REG_STATE(config, rw_clk_ctrl, bif, yes) | \
+              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.
+       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.
+       move.d  CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0
+       move.d  REG_ADDR(gio, regi_gio, rw_pa_dout), $r1
+       move.d  $r0, [$r1]
+
+       move.d  CONFIG_ETRAX_DEF_GIO_PA_OE, $r0
+       move.d  REG_ADDR(gio, regi_gio, rw_pa_oe), $r1
+       move.d  $r0, [$r1]
+
+       ;; Setup the stack to a suitably high address.
+       ;; 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.
+       ;; 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:
+       lapcq   ., $r5          ; get PC
+       and.d   0x7fffffff, $r5 ; strip any non-cache bit
+       move.d  $r5, $r0        ; save for later - flash 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
+
+       move.d  _edata, $r2     ; end destination
+1:     move.w  [$r0+], $r3
+       move.w  $r3, [$r1+]
+       cmp.d   $r2, $r1
+       bcs     1b
+       nop
+
+       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
+1:     move.w  $r0, [$r1+]
+       cmp.d   $r2, $r1
+       bcs     1b
+       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]
+
+       ;; Do the decompression and save compressed size in _inptr
+
+       jsr     decompress_kernel
+       nop
+
+       ;; 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:
+       ;; 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:
+       .dword  0               ; used by the decompressor
+_cmd_line_magic:
+       .dword 0
+_cmd_line_addr:
+       .dword 0
+is_nand_boot:
+       .dword  0
+
+#include "../../lib/hw_settings.S"
diff --git a/arch/cris/arch-v32/boot/compressed/misc.c b/arch/cris/arch-v32/boot/compressed/misc.c
new file mode 100644 (file)
index 0000000..5464423
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * 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
+ * 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.
+ * it is the same address we use when we network load an uncompressed
+ * image into DRAM, and it is the address the kernel is linked to live
+ * at by vmlinux.lds.S
+ */
+
+#define KERNEL_LOAD_ADR 0x40004000
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/ser_defs.h>
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args)  args
+#define STATIC static
+
+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))
+
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+#define WSIZE 0x8000           /* Window size must be at least 32k, */
+                               /* and a power of two */
+
+static uch *inbuf;          /* input buffer */
+static uch window[WSIZE];    /* Sliding window buffer */
+
+unsigned inptr = 0;    /* index of next byte to be processed in inbuf
+                        * After decompression it will contain the
+                        * compressed size, and head.S will read it.
+                        */
+
+static unsigned outcnt = 0;  /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+#define get_byte() inbuf[inptr++]
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+static int  fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern char *input_data;  /* lives in head.S */
+
+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)
+{
+       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;
+}
+
+static void free(void *where)
+{      /* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+       *ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+       free_mem_ptr = (long) *ptr;
+}
+
+/* decompressor info and error messages to serial console */
+
+static inline void
+serout(const char *s, reg_scope_instances regi_ser)
+{
+       reg_ser_rs_stat_din rs;
+       reg_ser_rw_dout dout = {.data = *s};
+
+       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);
+}
+
+static void
+puts(const char *s)
+{
+#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL
+       while (*s) {
+#ifdef CONFIG_ETRAX_DEBUG_PORT0
+               serout(s, regi_ser0);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT1
+               serout(s, regi_ser1);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT2
+               serout(s, regi_ser2);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT3
+               serout(s, regi_ser3);
+#endif
+               *s++;
+       }
+/* CONFIG_ETRAX_DEBUG_PORT_NULL */
+#endif
+}
+
+void*
+memset(void* s, int c, size_t n)
+{
+       int i;
+       char *ss = (char*)s;
+
+       for (i=0;i<n;i++) ss[i] = c;
+}
+
+void*
+memcpy(void* __dest, __const void* __src,
+                           size_t __n)
+{
+       int i;
+       char *d = (char *)__dest, *s = (char *)__src;
+
+       for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+
+static void
+flush_window()
+{
+    ulg c = crc;         /* temporary variable */
+    unsigned n;
+    uch *in, *out, ch;
+
+    in = window;
+    out = &output_data[output_ptr];
+    for (n = 0; n < outcnt; n++) {
+           ch = *out++ = *in++;
+           c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+    }
+    crc = c;
+    bytes_out += (ulg)outcnt;
+    output_ptr += (ulg)outcnt;
+    outcnt = 0;
+}
+
+static void
+error(char *x)
+{
+       puts("\n\n");
+       puts(x);
+       puts("\n\n -- System halted\n");
+
+       while(1);       /* Halt */
+}
+
+void
+setup_normal_output_buffer()
+{
+       output_data = (char *)KERNEL_LOAD_ADR;
+}
+
+static inline void
+serial_setup(reg_scope_instances regi_ser)
+{
+       reg_ser_rw_xoff xoff;
+       reg_ser_rw_tr_ctrl tr_ctrl;
+       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. */
+       tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+       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.base_freq = regk_ser_f29_493;
+       rec_ctrl.base_freq = regk_ser_f29_493;
+       tr_baud.div = (29493000 / 8) / 4800;
+       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);
+       REG_WR(ser, regi_ser, rw_rec_baud_div, rec_baud);
+}
+
+void
+decompress_kernel()
+{
+       char revision;
+
+       /* input_data is set in head.S */
+       inbuf = input_data;
+
+#ifdef CONFIG_ETRAX_DEBUG_PORT0
+       serial_setup(regi_ser0);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT1
+       serial_setup(regi_ser1);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT2
+       serial_setup(regi_ser2);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT3
+       serial_setup(regi_ser3);
+#endif
+
+       setup_normal_output_buffer();
+
+       makecrc();
+
+       __asm__ volatile ("move $vr,%0" : "=rm" (revision));
+       if (revision < 32)
+       {
+               puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n");
+               while(1);
+       }
+
+       puts("Uncompressing Linux...\n");
+       gunzip();
+       puts("Done. Now booting the kernel.\n");
+}
diff --git a/arch/cris/arch-v32/boot/rescue/Makefile b/arch/cris/arch-v32/boot/rescue/Makefile
new file mode 100644 (file)
index 0000000..f668a81
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# Makefile for rescue 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
+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:
diff --git a/arch/cris/arch-v32/boot/rescue/head.S b/arch/cris/arch-v32/boot/rescue/head.S
new file mode 100644 (file)
index 0000000..61ede5f
--- /dev/null
@@ -0,0 +1,39 @@
+/* $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.
+ */
+
+#include <linux/config.h>
+#include <asm/arch/hwregs/reg_map_asm.h>
+#include <asm/arch/hwregs/config_defs_asm.h>
+
+       .text
+
+       ;; Start clocks for used blocks.
+       move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
+       move.d [$r1], $r0
+       or.d   REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
+              REG_STATE(config, rw_clk_ctrl, bif, yes) | \
+              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
+
+#include "../../lib/nand_init.S"
+
+       ;; No NAND found
+       move.d  CONFIG_ETRAX_PTABLE_SECTOR, $r10
+       jump    $r10 ; Jump to decompresser
+       nop
+
+copy_complete:
+       move.d  0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10
+       jump    $r10 ; Jump to decompresser
+       nop
diff --git a/arch/cris/arch-v32/boot/rescue/rescue.ld b/arch/cris/arch-v32/boot/rescue/rescue.ld
new file mode 100644 (file)
index 0000000..42b11aa
--- /dev/null
@@ -0,0 +1,20 @@
+MEMORY
+       {
+       flash : ORIGIN = 0x00000000,
+               LENGTH = 0x00100000
+       }
+
+SECTIONS
+{
+       .text :
+       {
+               stext = . ;
+               *(.text)
+               etext = . ;
+       } > flash
+       .data :
+       {
+               *(.data)
+               edata = . ;
+       } > flash
+}
diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig
new file mode 100644 (file)
index 0000000..a33097f
--- /dev/null
@@ -0,0 +1,625 @@
+config ETRAX_ETHERNET
+       bool "Ethernet support"
+       depends on ETRAX_ARCH_V32
+       select NET_ETHERNET
+       help
+         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"
+
+config ETRAX_ETHERNET_IFACE1
+       depends on ETRAX_ETHERNET
+       bool "Enable network interface 1 (uses DMA6 and DMA7)"
+
+choice
+       prompt "Network LED behavior"
+       depends on ETRAX_ETHERNET
+       default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+
+config ETRAX_NETWORK_LED_ON_WHEN_LINK
+       bool "LED_on_when_link"
+       help
+         Selecting LED_on_when_link will light the LED when there is a
+         connection and will flash off when there is activity.
+
+         Selecting LED_on_when_activity will light the LED only when
+         there is activity.
+
+         This setting will also affect the behaviour of other activity LEDs
+         e.g. Bluetooth.
+
+config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+       bool "LED_on_when_activity"
+       help
+         Selecting LED_on_when_link will light the LED when there is a
+         connection and will flash off when there is activity.
+
+         Selecting LED_on_when_activity will light the LED only when
+         there is activity.
+
+         This setting will also affect the behaviour of other activity LEDs
+         e.g. Bluetooth.
+
+endchoice
+
+config ETRAXFS_SERIAL
+       bool "Serial-port support"
+       depends on ETRAX_ARCH_V32
+       help
+         Enables the ETRAX FS serial driver for ser0 (ttyS0)
+         You probably want this enabled.
+
+config ETRAX_SERIAL_PORT0
+       bool "Serial port 0 enabled"
+       depends on ETRAXFS_SERIAL
+       help
+         Enables the ETRAX FS serial driver for ser0 (ttyS0)
+         Normally you want this on. You can control what DMA channels to use
+         if you do not need DMA to something else.
+         ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
+
+choice
+       prompt "Ser0 DMA in channel "
+       depends on ETRAX_SERIAL_PORT0
+       default ETRAX_SERIAL_PORT0_NO_DMA_IN
+       help
+         What DMA channel to use for ser0.
+
+
+config ETRAX_SERIAL_PORT0_NO_DMA_IN
+       bool "Ser0 uses no DMA for input"
+       help
+         Do not use DMA for ser0 input.
+
+config ETRAX_SERIAL_PORT0_DMA7_IN
+       bool "Ser0 uses DMA7 for input"
+       depends on ETRAX_SERIAL_PORT0
+       help
+         Enables the DMA7 input channel for ser0 (ttyS0).
+         If you do not enable DMA, an interrupt for each character will be
+         used when receiveing data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+choice
+       prompt "Ser0 DMA out channel"
+       depends on ETRAX_SERIAL_PORT0
+       default ETRAX_SERIAL_PORT0_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT0_NO_DMA_OUT
+       bool "Ser0 uses no DMA for output"
+       help
+         Do not use DMA for ser0 output.
+
+config ETRAX_SERIAL_PORT0_DMA6_OUT
+       bool "Ser0 uses DMA6 for output"
+       depends on ETRAX_SERIAL_PORT0
+       help
+         Enables the DMA6 output channel for ser0 (ttyS0).
+         If you do not enable DMA, an interrupt for each character will be
+         used when transmitting data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+config ETRAX_SER0_DTR_BIT
+       string "Ser 0 DTR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_RI_BIT
+       string "Ser 0 RI bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_DSR_BIT
+       string "Ser 0 DSR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_CD_BIT
+       string "Ser 0 CD bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SERIAL_PORT1
+       bool "Serial port 1 enabled"
+       depends on ETRAXFS_SERIAL
+       help
+         Enables the ETRAX FS serial driver for ser1 (ttyS1).
+
+choice
+       prompt "Ser1 DMA in channel "
+       depends on ETRAX_SERIAL_PORT1
+       default ETRAX_SERIAL_PORT1_NO_DMA_IN
+       help
+         What DMA channel to use for ser1.
+
+
+config ETRAX_SERIAL_PORT1_NO_DMA_IN
+       bool "Ser1 uses no DMA for input"
+       help
+         Do not use DMA for ser1 input.
+
+config ETRAX_SERIAL_PORT1_DMA5_IN
+       bool "Ser1 uses DMA5 for input"
+       depends on ETRAX_SERIAL_PORT1
+       help
+         Enables the DMA5 input channel for ser1 (ttyS1).
+         If you do not enable DMA, an interrupt for each character will be
+         used when receiveing data.
+         Normally you want this on, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+choice
+       prompt "Ser1 DMA out channel "
+       depends on ETRAX_SERIAL_PORT1
+       default ETRAX_SERIAL_PORT1_NO_DMA_OUT
+       help
+         What DMA channel to use for ser1.
+
+config ETRAX_SERIAL_PORT1_NO_DMA_OUT
+       bool "Ser1 uses no DMA for output"
+       help
+         Do not use DMA for ser1 output.
+
+config ETRAX_SERIAL_PORT1_DMA4_OUT
+       bool "Ser1 uses DMA4 for output"
+       depends on ETRAX_SERIAL_PORT1
+       help
+         Enables the DMA4 output channel for ser1 (ttyS1).
+         If you do not enable DMA, an interrupt for each character will be
+         used when transmitting data.
+         Normally you want this on, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+config ETRAX_SER1_DTR_BIT
+       string "Ser 1 DTR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_RI_BIT
+       string "Ser 1 RI bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_DSR_BIT
+       string "Ser 1 DSR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_CD_BIT
+       string "Ser 1 CD bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SERIAL_PORT2
+       bool "Serial port 2 enabled"
+       depends on ETRAXFS_SERIAL
+       help
+         Enables the ETRAX FS serial driver for ser2 (ttyS2).
+
+choice
+       prompt "Ser2 DMA in channel "
+       depends on ETRAX_SERIAL_PORT2
+       default ETRAX_SERIAL_PORT2_NO_DMA_IN
+       help
+         What DMA channel to use for ser2.
+
+
+config ETRAX_SERIAL_PORT2_NO_DMA_IN
+       bool "Ser2 uses no DMA for input"
+       help
+         Do not use DMA for ser2 input.
+
+config ETRAX_SERIAL_PORT2_DMA3_IN
+       bool "Ser2 uses DMA3 for input"
+       depends on ETRAX_SERIAL_PORT2
+       help
+         Enables the DMA3 input channel for ser2 (ttyS2).
+         If you do not enable DMA, an interrupt for each character will be
+         used when receiveing data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+choice
+       prompt "Ser2 DMA out channel"
+       depends on ETRAX_SERIAL_PORT2
+       default ETRAX_SERIAL_PORT2_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT2_NO_DMA_OUT
+       bool "Ser2 uses no DMA for output"
+       help
+         Do not use DMA for ser2 output.
+
+config ETRAX_SERIAL_PORT2_DMA2_OUT
+       bool "Ser2 uses DMA2 for output"
+       depends on ETRAX_SERIAL_PORT2
+       help
+         Enables the DMA2 output channel for ser2 (ttyS2).
+         If you do not enable DMA, an interrupt for each character will be
+         used when transmitting data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+config ETRAX_SER2_DTR_BIT
+       string "Ser 2 DTR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_RI_BIT
+       string "Ser 2 RI bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_DSR_BIT
+       string "Ser 2 DSR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_CD_BIT
+       string "Ser 2 CD bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SERIAL_PORT3
+       bool "Serial port 3 enabled"
+       depends on ETRAXFS_SERIAL
+       help
+         Enables the ETRAX FS serial driver for ser3 (ttyS3).
+
+choice
+       prompt "Ser3 DMA in channel "
+       depends on ETRAX_SERIAL_PORT3
+       default ETRAX_SERIAL_PORT3_NO_DMA_IN
+       help
+         What DMA channel to use for ser3.
+
+
+config ETRAX_SERIAL_PORT3_NO_DMA_IN
+       bool "Ser3 uses no DMA for input"
+       help
+         Do not use DMA for ser3 input.
+
+config ETRAX_SERIAL_PORT3_DMA9_IN
+       bool "Ser3 uses DMA9 for input"
+       depends on ETRAX_SERIAL_PORT3
+       help
+         Enables the DMA9 input channel for ser3 (ttyS3).
+         If you do not enable DMA, an interrupt for each character will be
+         used when receiveing data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+choice
+       prompt "Ser3 DMA out channel"
+       depends on ETRAX_SERIAL_PORT3
+       default ETRAX_SERIAL_PORT3_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT3_NO_DMA_OUT
+       bool "Ser3 uses no DMA for output"
+       help
+         Do not use DMA for ser3 output.
+
+config ETRAX_SERIAL_PORT3_DMA8_OUT
+       bool "Ser3 uses DMA8 for output"
+       depends on ETRAX_SERIAL_PORT3
+       help
+         Enables the DMA8 output channel for ser3 (ttyS3).
+         If you do not enable DMA, an interrupt for each character will be
+         used when transmitting data.
+         Normally you want to use DMA, unless you use the DMA channel for
+         something else.
+
+endchoice
+
+config ETRAX_SER3_DTR_BIT
+       string "Ser 3 DTR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_RI_BIT
+       string "Ser 3 RI bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_DSR_BIT
+       string "Ser 3 DSR bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_CD_BIT
+       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 <http://www.hw.cz/english/docs/rs485/rs485.html>.
+
+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
+       help
+         Enables the ETRAX FS synchronous serial driver.
+
+config ETRAX_SYNCHRONOUS_SERIAL_PORT0
+         bool "Synchronous serial port 0 enabled"
+         depends on ETRAX_SYNCHRONOUS_SERIAL
+         help
+           Enabled synchronous serial port 0.
+
+config ETRAX_SYNCHRONOUS_SERIAL0_DMA
+         bool "Enable DMA on synchronous serial port 0."
+         depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0
+         help
+           A synchronous serial port can run in manual or DMA mode.
+           Selecting this option will make it run in DMA mode.
+
+config ETRAX_SYNCHRONOUS_SERIAL_PORT1
+         bool "Synchronous serial port 1 enabled"
+         depends on ETRAX_SYNCHRONOUS_SERIAL
+         help
+           Enabled synchronous serial port 1.
+
+config ETRAX_SYNCHRONOUS_SERIAL1_DMA
+         bool "Enable DMA on synchronous serial port 1."
+         depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1
+         help
+           A synchronous serial port can run in manual or DMA mode.
+           Selecting this option will make it run in DMA mode.
+
+config ETRAX_PTABLE_SECTOR
+       int "Byte-offset of partition table sector"
+       depends on ETRAX_AXISFLASHMAP
+       default "65536"
+       help
+         Byte-offset of the partition table in the first flash chip.
+         The default value is 64kB and should not be changed unless
+         you know exactly what you are doing. The only valid reason
+         for changing this is when the flash block size is bigger
+         than 64kB (e.g. when using two parallel 16 bit flashes).
+
+config ETRAX_NANDFLASH
+       bool "NAND flash support"
+       depends on ETRAX_ARCH_V32
+       select MTD_NAND
+       select MTD_NAND_IDS
+       help
+         This option enables MTD mapping of NAND flash devices.  Needed to use
+         NAND flash memories.  If unsure, say Y.
+
+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.
+
+config ETRAX_I2C_DATA_PORT
+       string "I2C data pin"
+       depends on ETRAX_I2C
+       help
+         The pin to use for I2C data.
+
+config ETRAX_I2C_CLK_PORT
+       string "I2C clock pin"
+       depends on ETRAX_I2C
+       help
+         The pin to use for I2C clock.
+
+config ETRAX_RTC
+       bool "Real Time Clock support"
+       depends on ETRAX_ARCH_V32
+       help
+         Enabled RTC support.
+
+choice
+       prompt "RTC chip"
+       depends on ETRAX_RTC
+       default ETRAX_PCF8563
+
+config ETRAX_PCF8563
+       bool "PCF8563"
+       help
+         Philips PCF8563 RTC
+
+endchoice
+
+config ETRAX_GPIO
+       bool "GPIO support"
+       depends on ETRAX_ARCH_V32
+       ---help---
+         Enables the ETRAX general port device (major 120, minors 0-4).
+         You can use this driver to access the general port bits. It supports
+         these ioctl's:
+         #include <linux/etraxgpio.h>
+         fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
+         ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
+         ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
+         err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val);
+         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 "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.
+
+config ETRAX_PA_CHANGEABLE_DIR
+       hex "PA user changeable dir mask"
+       depends on ETRAX_GPIO
+       default "0x00"
+       help
+         This is a bitmask (8 bits) with information of what bits in PA that a
+         user can change direction on using ioctl's.
+         Bit set = changeable.
+         You probably want 0x00 here, but it depends on your hardware.
+
+config ETRAX_PA_CHANGEABLE_BITS
+       hex "PA user changeable bits mask"
+       depends on ETRAX_GPIO
+       default "0x00"
+       help
+         This is a bitmask (8 bits) with information of what bits in PA
+         that a user can change the value on using ioctl's.
+         Bit set = changeable.
+
+config ETRAX_PB_CHANGEABLE_DIR
+       hex "PB user changeable dir mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PB
+         that a user can change direction on using ioctl's.
+         Bit set = changeable.
+         You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PB_CHANGEABLE_BITS
+       hex "PB user changeable bits mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PB
+         that a user can change the value on using ioctl's.
+         Bit set = changeable.
+
+config ETRAX_PC_CHANGEABLE_DIR
+       hex "PC user changeable dir mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PC
+         that a user can change direction on using ioctl's.
+         Bit set = changeable.
+         You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PC_CHANGEABLE_BITS
+       hex "PC user changeable bits mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PC
+         that a user can change the value on using ioctl's.
+         Bit set = changeable.
+
+config ETRAX_PD_CHANGEABLE_DIR
+       hex "PD user changeable dir mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PD
+         that a user can change direction on using ioctl's.
+         Bit set = changeable.
+         You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PD_CHANGEABLE_BITS
+       hex "PD user changeable bits mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PD
+         that a user can change the value on using ioctl's.
+         Bit set = changeable.
+
+config ETRAX_PE_CHANGEABLE_DIR
+       hex "PE user changeable dir mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PE
+         that a user can change direction on using ioctl's.
+         Bit set = changeable.
+         You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PE_CHANGEABLE_BITS
+       hex "PE user changeable bits mask"
+       depends on ETRAX_GPIO
+       default "0x00000"
+       help
+         This is a bitmask (18 bits) with information of what bits in PE
+         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
+       select IDE
+       select BLK_DEV_IDE
+       select BLK_DEV_IDEDISK
+       select BLK_DEV_IDECD
+       select BLK_DEV_IDEDMA
+       help
+         Enables the ETRAX IDE driver.
+
+config ETRAX_CARDBUS
+        bool "Cardbus support"
+        depends on ETRAX_ARCH_V32
+        select PCCARD
+        select CARDBUS
+        select HOTPLUG
+        select PCCARD_NONSTATIC
+        help
+        Enabled the ETRAX Carbus driver.
+
+config PCI
+       bool
+       depends on ETRAX_CARDBUS
+       default y
+
+config ETRAX_IOP_FW_LOAD
+       tristate "IO-processor hotplug firmware loading support"
+       depends on ETRAX_ARCH_V32
+       select FW_LOADER
+       help
+         Enables IO-processor hotplug firmware loading support.
+
+config ETRAX_STREAMCOPROC
+       tristate "Stream co-processor driver enabled"
+       depends on ETRAX_ARCH_V32
+       help
+         This option enables a driver for the stream co-processor
+         for cryptographic operations.
diff --git a/arch/cris/arch-v32/drivers/Makefile b/arch/cris/arch-v32/drivers/Makefile
new file mode 100644 (file)
index 0000000..a359cd2
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for Etrax-specific drivers
+#
+
+obj-$(CONFIG_ETRAX_STREAMCOPROC)        += cryptocop.o
+obj-$(CONFIG_ETRAX_AXISFLASHMAP)        += axisflashmap.o
+obj-$(CONFIG_ETRAX_NANDFLASH)           += nandflash.o
+obj-$(CONFIG_ETRAX_GPIO)               += gpio.o
+obj-$(CONFIG_ETRAX_IOP_FW_LOAD)         += iop_fw_load.o
+obj-$(CONFIG_ETRAX_PCF8563)            += pcf8563.o
+obj-$(CONFIG_ETRAX_I2C)                        += i2c.o
+obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
+obj-$(CONFIG_PCI)                      += pci/
diff --git a/arch/cris/arch-v32/drivers/axisflashmap.c b/arch/cris/arch-v32/drivers/axisflashmap.c
new file mode 100644 (file)
index 0000000..78ed52b
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Physical mapping layer for MTD using the Axis partitiontable format
+ *
+ * Copyright (c) 2001, 2002, 2003 Axis Communications AB
+ *
+ * This file is under the GPL.
+ *
+ * First partition is always sector 0 regardless of if we find a partitiontable
+ * or not. In the start of the next sector, there can be a partitiontable that
+ * tells us what other partitions to define. If there isn't, we use a default
+ * partition split defined below.
+ *
+ * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5
+ * with minor changes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <linux/mtd/concat.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/mtdram.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/arch/hwregs/config_defs.h>
+#include <asm/axisflashmap.h>
+#include <asm/mmu.h>
+
+#define MEM_CSE0_SIZE (0x04000000)
+#define MEM_CSE1_SIZE (0x04000000)
+
+#define FLASH_UNCACHED_ADDR  KSEG_E
+#define FLASH_CACHED_ADDR    KSEG_F
+
+#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
+#endif
+
+/* From head.S */
+extern unsigned long romfs_start, romfs_length, romfs_in_flash;
+
+/* The master mtd for the entire flash. */
+struct mtd_info* axisflash_mtd = NULL;
+
+/* Map driver functions. */
+
+static map_word flash_read(struct map_info *map, unsigned long ofs)
+{
+       map_word tmp;
+       tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs);
+       return tmp;
+}
+
+static void flash_copy_from(struct map_info *map, void *to,
+                           unsigned long from, ssize_t len)
+{
+       memcpy(to, (void *)(map->map_priv_1 + from), len);
+}
+
+static void flash_write(struct map_info *map, map_word d, unsigned long adr)
+{
+       *(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0];
+}
+
+/*
+ * The map for chip select e0.
+ *
+ * We run into tricky coherence situations if we mix cached with uncached
+ * accesses to we only use the uncached version here.
+ *
+ * The size field is the total size where the flash chips may be mapped on the
+ * chip select. MTD probes should find all devices there and it does not matter
+ * if there are unmapped gaps or aliases (mirrors of flash devices). The MTD
+ * probes will ignore them.
+ *
+ * The start address in map_priv_1 is in virtual memory so we cannot use
+ * MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start
+ * address of cse0.
+ */
+static struct map_info map_cse0 = {
+       .name = "cse0",
+       .size = MEM_CSE0_SIZE,
+       .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
+       .read = flash_read,
+       .copy_from = flash_copy_from,
+       .write = flash_write,
+       .map_priv_1 = FLASH_UNCACHED_ADDR
+};
+
+/*
+ * The map for chip select e1.
+ *
+ * If there was a gap between cse0 and cse1, map_priv_1 would get the wrong
+ * address, but there isn't.
+ */
+static struct map_info map_cse1 = {
+       .name = "cse1",
+       .size = MEM_CSE1_SIZE,
+       .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
+       .read = flash_read,
+       .copy_from = flash_copy_from,
+       .write = flash_write,
+       .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
+
+/*
+ * 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)
+       }
+};
+
+/* Initialize the ones normally used. */
+static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
+       {
+               .name = "part0",
+               .size = CONFIG_ETRAX_PTABLE_SECTOR,
+               .offset = 0
+       },
+       {
+               .name = "part1",
+               .size = 0,
+               .offset = 0
+       },
+       {
+               .name = "part2",
+               .size = 0,
+               .offset = 0
+       },
+       {
+               .name = "part3",
+               .size = 0,
+               .offset = 0
+       },
+       {
+               .name = "part4",
+               .size = 0,
+               .offset = 0
+       },
+       {
+               .name = "part5",
+               .size = 0,
+               .offset = 0
+       },
+       {
+               .name = "part6",
+               .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).
+ */
+static struct mtd_info *probe_cs(struct map_info *map_cs)
+{
+       struct mtd_info *mtd_cs = NULL;
+
+       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
+       if (!mtd_cs) {
+               mtd_cs = do_map_probe("cfi_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.
+ *
+ * The only known restriction to how you can mount your chips is that each
+ * chip select must hold similar flash chips. But you need external hardware
+ * to do that anyway and you can put totally different chips on cse0 and cse1
+ * so it isn't really much of a restriction.
+ */
+extern struct mtd_info* __init crisv32_nand_flash_probe (void);
+static struct mtd_info *flash_probe(void)
+{
+       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];
+       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) {
+               /* No chip found. */
+               return NULL;
+       }
+
+       if (count > 1) {
+#ifdef CONFIG_MTD_CONCAT
+               /* Since the concatenation layer adds a small overhead we
+                * could try to figure out if the chips in cse0 and cse1 are
+                * identical and reprobe the whole cse0+cse1 window. But since
+                * flash chips are slow, the overhead is relatively small.
+                * So we use the MTD concatenation layer instead of further
+                * complicating the probing procedure.
+                */
+               mtd_total = mtd_concat_create(mtds,
+                                             count,
+                                             "cse0+cse1+nand");
+#else
+               printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel "
+                      "(mis)configuration!\n", map_cse0.name, map_cse1.name);
+               mtd_toal = NULL;
+#endif
+               if (!mtd_total) {
+                       printk(KERN_ERR "%s and %s: Concatenation failed!\n",
+                              map_cse0.name, map_cse1.name);
+
+                       /* 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;
+
+       }
+
+       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;
+       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];
+       size_t len;
+
+#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);
+
+       if (!mymtd) {
+               /* 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;
+       }
+       pidx++;  /* First partition is always set to the default. */
+
+       if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
+           && (ptable_head->size <
+               (MAX_PARTITIONS * sizeof(struct partitiontable_entry) +
+               PARTITIONTABLE_END_MARKER_SIZE))
+           && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) +
+                                 ptable_head->size -
+                                 PARTITIONTABLE_END_MARKER_SIZE)
+               == PARTITIONTABLE_END_MARKER)) {
+               /* 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) +
+                        ptable_head->size);
+               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));
+
+               /* Lets be PARANOID, and check the checksum. */
+               p = (unsigned char*) ptable;
+
+               while (p <= (unsigned char*)max_addr) {
+                       csum += *p++;
+                       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",
+                      (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.
+                */
+               while (ptable_ok
+                      && ptable->offset != 0xffffffff
+                      && ptable < max_addr
+                      && pidx < MAX_PARTITIONS) {
+
+                       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);
+                       pidx++;
+                       ptable++;
+               }
+               use_default_ptable = !ptable_ok;
+       }
+
+       if (romfs_in_flash) {
+               /* Add an overlapping device for the root partition (romfs). */
+
+               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;
+               }
+
+               axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
+
+               printk(KERN_INFO
+                       " Adding readonly flash partition for romfs image:\n");
+               printk(pmsg, pidx, axis_partitions[pidx].offset,
+                      axis_partitions[pidx].size);
+               pidx++;
+       }
+
+        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);
+               }
+
+               if (err) {
+                       panic("axisflashmap could not add MTD partitions!\n");
+               }
+       }
+/* CONFIG_EXTRAXFS_SIM */
+#endif
+
+       if (!romfs_in_flash) {
+               /* Create an RAM device for the root partition (romfs). */
+
+#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
+               /* No use trying to boot this kernel from RAM. Panic! */
+               printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
+                      "device due to kernel (mis)configuration!\n");
+               panic("This kernel cannot boot from RAM!\n");
+#else
+               struct mtd_info *mtd_ram;
+
+               mtd_ram = (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);
+
+               err = mtdram_init_device(mtd_ram, (void*)romfs_start,
+                                        romfs_length, "romfs");
+               if (err) {
+                       panic("axisflashmap could not initialize MTD RAM "
+                             "device!\n");
+               }
+#endif
+       }
+
+       return err;
+}
+
+/* This adds the above to the kernels init-call chain. */
+module_init(init_axis_flash);
+
+EXPORT_SYMBOL(axisflash_mtd);
diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c
new file mode 100644 (file)
index 0000000..ca72076
--- /dev/null
@@ -0,0 +1,3522 @@
+/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $
+ *
+ * Stream co-processor driver for the ETRAX FS
+ *
+ *    Copyright (C) 2003-2005  Axis Communications AB
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#include <asm/signal.h>
+#include <asm/irq.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/hwregs/dma.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/intr_vect_defs.h>
+
+#include <asm/arch/hwregs/strcop.h>
+#include <asm/arch/hwregs/strcop_defs.h>
+#include <asm/arch/cryptocop.h>
+
+
+
+#define DESCR_ALLOC_PAD  (31)
+
+struct cryptocop_dma_desc {
+       char *free_buf; /* If non-null will be kfreed in free_cdesc() */
+       dma_descr_data *dma_descr;
+
+       unsigned char dma_descr_buf[sizeof(dma_descr_data) + DESCR_ALLOC_PAD];
+
+       unsigned int from_pool:1; /* If 1 'allocated' from the descriptor pool. */
+       struct cryptocop_dma_desc *next;
+};
+
+
+struct cryptocop_int_operation{
+       void                        *alloc_ptr;
+       cryptocop_session_id        sid;
+
+       dma_descr_context           ctx_out;
+       dma_descr_context           ctx_in;
+
+       /* DMA descriptors allocated by driver. */
+       struct cryptocop_dma_desc   *cdesc_out;
+       struct cryptocop_dma_desc   *cdesc_in;
+
+       /* Strcop config to use. */
+       cryptocop_3des_mode         tdes_mode;
+       cryptocop_csum_type         csum_mode;
+
+       /* DMA descrs provided by consumer. */
+       dma_descr_data              *ddesc_out;
+       dma_descr_data              *ddesc_in;
+};
+
+
+struct cryptocop_tfrm_ctx {
+       cryptocop_tfrm_id tid;
+       unsigned int blocklength;
+
+       unsigned int start_ix;
+
+       struct cryptocop_tfrm_cfg *tcfg;
+       struct cryptocop_transform_ctx *tctx;
+
+       unsigned char previous_src;
+       unsigned char current_src;
+
+       /* Values to use in metadata out. */
+       unsigned char hash_conf;
+       unsigned char hash_mode;
+       unsigned char ciph_conf;
+       unsigned char cbcmode;
+       unsigned char decrypt;
+
+       unsigned int requires_padding:1;
+       unsigned int strict_block_length:1;
+       unsigned int active:1;
+       unsigned int done:1;
+       size_t consumed;
+       size_t produced;
+
+       /* Pad (input) descriptors to put in the DMA out list when the transform
+        * output is put on the DMA in list. */
+       struct cryptocop_dma_desc *pad_descs;
+
+       struct cryptocop_tfrm_ctx *prev_src;
+       struct cryptocop_tfrm_ctx *curr_src;
+
+       /* Mapping to HW. */
+       unsigned char unit_no;
+};
+
+
+struct cryptocop_private{
+       cryptocop_session_id sid;
+       struct cryptocop_private *next;
+};
+
+/* Session list. */
+
+struct cryptocop_transform_ctx{
+       struct cryptocop_transform_init init;
+       unsigned char dec_key[CRYPTOCOP_MAX_KEY_LENGTH];
+       unsigned int dec_key_set:1;
+
+       struct cryptocop_transform_ctx *next;
+};
+
+
+struct cryptocop_session{
+       cryptocop_session_id sid;
+
+       struct cryptocop_transform_ctx *tfrm_ctx;
+
+       struct cryptocop_session *next;
+};
+
+/* Priority levels for jobs sent to the cryptocop.  Checksum operations from
+   kernel have highest priority since TCPIP stack processing must not
+   be a bottleneck. */
+typedef enum {
+       cryptocop_prio_kernel_csum = 0,
+       cryptocop_prio_kernel = 1,
+       cryptocop_prio_user = 2,
+       cryptocop_prio_no_prios = 3
+} cryptocop_queue_priority;
+
+struct cryptocop_prio_queue{
+       struct list_head jobs;
+       cryptocop_queue_priority prio;
+};
+
+struct cryptocop_prio_job{
+       struct list_head node;
+       cryptocop_queue_priority prio;
+
+       struct cryptocop_operation *oper;
+       struct cryptocop_int_operation *iop;
+};
+
+struct ioctl_job_cb_ctx {
+       unsigned int processed:1;
+};
+
+
+static struct cryptocop_session *cryptocop_sessions = NULL;
+spinlock_t cryptocop_sessions_lock;
+
+/* Next Session ID to assign. */
+static cryptocop_session_id next_sid = 1;
+
+/* Pad for checksum. */
+static const char csum_zero_pad[1] = {0x00};
+
+/* Trash buffer for mem2mem operations. */
+#define MEM2MEM_DISCARD_BUF_LENGTH  (512)
+static unsigned char mem2mem_discard_buf[MEM2MEM_DISCARD_BUF_LENGTH];
+
+/* Descriptor pool. */
+/* FIXME Tweak this value. */
+#define CRYPTOCOP_DESCRIPTOR_POOL_SIZE   (100)
+static struct cryptocop_dma_desc descr_pool[CRYPTOCOP_DESCRIPTOR_POOL_SIZE];
+static struct cryptocop_dma_desc *descr_pool_free_list;
+static int descr_pool_no_free;
+static spinlock_t descr_pool_lock;
+
+/* Lock to stop cryptocop to start processing of a new operation. The holder
+   of this lock MUST call cryptocop_start_job() after it is unlocked. */
+spinlock_t cryptocop_process_lock;
+
+static struct cryptocop_prio_queue cryptocop_job_queues[cryptocop_prio_no_prios];
+static spinlock_t cryptocop_job_queue_lock;
+static struct cryptocop_prio_job *cryptocop_running_job = NULL;
+static spinlock_t running_job_lock;
+
+/* The interrupt handler appends completed jobs to this list. The scehduled
+ * tasklet removes them upon sending the response to the crypto consumer. */
+static struct list_head cryptocop_completed_jobs;
+static spinlock_t cryptocop_completed_jobs_lock;
+
+DECLARE_WAIT_QUEUE_HEAD(cryptocop_ioc_process_wq);
+
+
+/** Local functions. **/
+
+static int cryptocop_open(struct inode *, struct file *);
+
+static int cryptocop_release(struct inode *, struct file *);
+
+static int cryptocop_ioctl(struct inode *inode, struct file *file,
+                          unsigned int cmd, unsigned long arg);
+
+static void cryptocop_start_job(void);
+
+static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation);
+static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation);
+
+static int cryptocop_job_queue_init(void);
+static void cryptocop_job_queue_close(void);
+
+static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);
+
+static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);
+
+static int transform_ok(struct cryptocop_transform_init *tinit);
+
+static struct cryptocop_session *get_session(cryptocop_session_id sid);
+
+static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid);
+
+static void delete_internal_operation(struct cryptocop_int_operation *iop);
+
+static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned  char *key, unsigned int keylength);
+
+static int init_stream_coprocessor(void);
+
+static void __exit exit_stream_coprocessor(void);
+
+/*#define LDEBUG*/
+#ifdef LDEBUG
+#define DEBUG(s) s
+#define DEBUG_API(s) s
+static void print_cryptocop_operation(struct cryptocop_operation *cop);
+static void print_dma_descriptors(struct cryptocop_int_operation *iop);
+static void print_strcop_crypto_op(struct strcop_crypto_op *cop);
+static void print_lock_status(void);
+static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op);
+#define assert(s) do{if (!(s)) panic(#s);} while(0);
+#else
+#define DEBUG(s)
+#define DEBUG_API(s)
+#define assert(s)
+#endif
+
+
+/* Transform constants. */
+#define DES_BLOCK_LENGTH   (8)
+#define AES_BLOCK_LENGTH   (16)
+#define MD5_BLOCK_LENGTH   (64)
+#define SHA1_BLOCK_LENGTH  (64)
+#define CSUM_BLOCK_LENGTH  (2)
+#define MD5_STATE_LENGTH   (16)
+#define SHA1_STATE_LENGTH  (20)
+
+/* The device number. */
+#define CRYPTOCOP_MAJOR    (254)
+#define CRYPTOCOP_MINOR    (0)
+
+
+
+struct file_operations cryptocop_fops = {
+       owner: THIS_MODULE,
+       open: cryptocop_open,
+       release: cryptocop_release,
+       ioctl: cryptocop_ioctl
+};
+
+
+static void free_cdesc(struct cryptocop_dma_desc *cdesc)
+{
+       DEBUG(printk("free_cdesc: cdesc 0x%p, from_pool=%d\n", cdesc, cdesc->from_pool));
+       if (cdesc->free_buf) kfree(cdesc->free_buf);
+
+       if (cdesc->from_pool) {
+               unsigned long int flags;
+               spin_lock_irqsave(&descr_pool_lock, flags);
+               cdesc->next = descr_pool_free_list;
+               descr_pool_free_list = cdesc;
+               ++descr_pool_no_free;
+               spin_unlock_irqrestore(&descr_pool_lock, flags);
+       } else {
+               kfree(cdesc);
+       }
+}
+
+
+static struct cryptocop_dma_desc *alloc_cdesc(int alloc_flag)
+{
+       int use_pool = (alloc_flag & GFP_ATOMIC) ? 1 : 0;
+       struct cryptocop_dma_desc *cdesc;
+
+       if (use_pool) {
+               unsigned long int flags;
+               spin_lock_irqsave(&descr_pool_lock, flags);
+               if (!descr_pool_free_list) {
+                       spin_unlock_irqrestore(&descr_pool_lock, flags);
+                       DEBUG_API(printk("alloc_cdesc: pool is empty\n"));
+                       return NULL;
+               }
+               cdesc = descr_pool_free_list;
+               descr_pool_free_list = descr_pool_free_list->next;
+               --descr_pool_no_free;
+               spin_unlock_irqrestore(&descr_pool_lock, flags);
+               cdesc->from_pool = 1;
+       } else {
+               cdesc = kmalloc(sizeof(struct cryptocop_dma_desc), alloc_flag);
+               if (!cdesc) {
+                       DEBUG_API(printk("alloc_cdesc: kmalloc\n"));
+                       return NULL;
+               }
+               cdesc->from_pool = 0;
+       }
+       cdesc->dma_descr = (dma_descr_data*)(((unsigned long int)cdesc + offsetof(struct cryptocop_dma_desc, dma_descr_buf) + DESCR_ALLOC_PAD) & ~0x0000001F);
+
+       cdesc->next = NULL;
+
+       cdesc->free_buf = NULL;
+       cdesc->dma_descr->out_eop = 0;
+       cdesc->dma_descr->in_eop = 0;
+       cdesc->dma_descr->intr = 0;
+       cdesc->dma_descr->eol = 0;
+       cdesc->dma_descr->wait = 0;
+       cdesc->dma_descr->buf = NULL;
+       cdesc->dma_descr->after = NULL;
+
+       DEBUG_API(printk("alloc_cdesc: return 0x%p, cdesc->dma_descr=0x%p, from_pool=%d\n", cdesc, cdesc->dma_descr, cdesc->from_pool));
+       return cdesc;
+}
+
+
+static void setup_descr_chain(struct cryptocop_dma_desc *cd)
+{
+       DEBUG(printk("setup_descr_chain: entering\n"));
+       while (cd) {
+               if (cd->next) {
+                       cd->dma_descr->next = (dma_descr_data*)virt_to_phys(cd->next->dma_descr);
+               } else {
+                       cd->dma_descr->next = NULL;
+               }
+               cd = cd->next;
+       }
+       DEBUG(printk("setup_descr_chain: exit\n"));
+}
+
+
+/* Create a pad descriptor for the transform.
+ * Return -1 for error, 0 if pad created. */
+static int create_pad_descriptor(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **pad_desc, int alloc_flag)
+{
+       struct cryptocop_dma_desc        *cdesc = NULL;
+       int                              error = 0;
+       struct strcop_meta_out           mo = {
+               .ciphsel = src_none,
+               .hashsel = src_none,
+               .csumsel = src_none
+       };
+       char                             *pad;
+       size_t                           plen;
+
+       DEBUG(printk("create_pad_descriptor: start.\n"));
+       /* Setup pad descriptor. */
+
+       DEBUG(printk("create_pad_descriptor: setting up padding.\n"));
+       cdesc = alloc_cdesc(alloc_flag);
+       if (!cdesc){
+               DEBUG_API(printk("create_pad_descriptor: alloc pad desc\n"));
+               goto error_cleanup;
+       }
+       switch (tc->unit_no) {
+       case src_md5:
+               error = create_md5_pad(alloc_flag, tc->consumed, &pad, &plen);
+               if (error){
+                       DEBUG_API(printk("create_pad_descriptor: create_md5_pad_failed\n"));
+                       goto error_cleanup;
+               }
+               cdesc->free_buf = pad;
+               mo.hashsel = src_dma;
+               mo.hashconf = tc->hash_conf;
+               mo.hashmode = tc->hash_mode;
+               break;
+       case src_sha1:
+               error = create_sha1_pad(alloc_flag, tc->consumed, &pad, &plen);
+               if (error){
+                       DEBUG_API(printk("create_pad_descriptor: create_sha1_pad_failed\n"));
+                       goto error_cleanup;
+               }
+               cdesc->free_buf = pad;
+               mo.hashsel = src_dma;
+               mo.hashconf = tc->hash_conf;
+               mo.hashmode = tc->hash_mode;
+               break;
+       case src_csum:
+               if (tc->consumed % tc->blocklength){
+                       pad = (char*)csum_zero_pad;
+                       plen = 1;
+               } else {
+                       pad = (char*)cdesc; /* Use any pointer. */
+                       plen = 0;
+               }
+               mo.csumsel = src_dma;
+               break;
+       }
+       cdesc->dma_descr->wait = 1;
+       cdesc->dma_descr->out_eop = 1; /* Since this is a pad output is pushed.  EOP is ok here since the padded unit is the only one active. */
+       cdesc->dma_descr->buf = (char*)virt_to_phys((char*)pad);
+       cdesc->dma_descr->after = cdesc->dma_descr->buf + plen;
+
+       cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+       *pad_desc = cdesc;
+
+       return 0;
+
+ error_cleanup:
+       if (cdesc) free_cdesc(cdesc);
+       return -1;
+}
+
+
+static int setup_key_dl_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **kd, int alloc_flag)
+{
+       struct cryptocop_dma_desc  *key_desc = alloc_cdesc(alloc_flag);
+       struct strcop_meta_out     mo = {0};
+
+       DEBUG(printk("setup_key_dl_desc\n"));
+
+       if (!key_desc) {
+               DEBUG_API(printk("setup_key_dl_desc: failed descriptor allocation.\n"));
+               return -ENOMEM;
+       }
+
+       /* Download key. */
+       if ((tc->tctx->init.alg == cryptocop_alg_aes) && (tc->tcfg->flags & CRYPTOCOP_DECRYPT)) {
+               /* Precook the AES decrypt key. */
+               if (!tc->tctx->dec_key_set){
+                       get_aes_decrypt_key(tc->tctx->dec_key, tc->tctx->init.key, tc->tctx->init.keylen);
+                       tc->tctx->dec_key_set = 1;
+               }
+               key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->dec_key);
+               key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;
+       } else {
+               key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->init.key);
+               key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;
+       }
+       /* Setup metadata. */
+       mo.dlkey = 1;
+       switch (tc->tctx->init.keylen) {
+       case 64:
+               mo.decrypt = 0;
+               mo.hashmode = 0;
+               break;
+       case 128:
+               mo.decrypt = 0;
+               mo.hashmode = 1;
+               break;
+       case 192:
+               mo.decrypt = 1;
+               mo.hashmode = 0;
+               break;
+       case 256:
+               mo.decrypt = 1;
+               mo.hashmode = 1;
+               break;
+       default:
+               break;
+       }
+       mo.ciphsel = mo.hashsel = mo.csumsel = src_none;
+       key_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+
+       key_desc->dma_descr->out_eop = 1;
+       key_desc->dma_descr->wait = 1;
+       key_desc->dma_descr->intr = 0;
+
+       *kd = key_desc;
+       return 0;
+}
+
+static int setup_cipher_iv_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)
+{
+       struct cryptocop_dma_desc  *iv_desc = alloc_cdesc(alloc_flag);
+       struct strcop_meta_out     mo = {0};
+
+       DEBUG(printk("setup_cipher_iv_desc\n"));
+
+       if (!iv_desc) {
+               DEBUG_API(printk("setup_cipher_iv_desc: failed CBC IV descriptor allocation.\n"));
+               return -ENOMEM;
+       }
+       /* Download IV. */
+       iv_desc->dma_descr->buf = (char*)virt_to_phys(tc->tcfg->iv);
+       iv_desc->dma_descr->after = iv_desc->dma_descr->buf + tc->blocklength;
+
+       /* Setup metadata. */
+       mo.hashsel = mo.csumsel = src_none;
+       mo.ciphsel = src_dma;
+       mo.ciphconf = tc->ciph_conf;
+       mo.cbcmode = tc->cbcmode;
+
+       iv_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+
+       iv_desc->dma_descr->out_eop = 0;
+       iv_desc->dma_descr->wait = 1;
+       iv_desc->dma_descr->intr = 0;
+
+       *id = iv_desc;
+       return 0;
+}
+
+/* Map the ouput length of the transform to operation output starting on the inject index. */
+static int create_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)
+{
+       int                        err = 0;
+       struct cryptocop_dma_desc  head = {0};
+       struct cryptocop_dma_desc  *outdesc = &head;
+       size_t                     iov_offset = 0;
+       size_t                     out_ix = 0;
+       int                        outiov_ix = 0;
+       struct strcop_meta_in      mi = {0};
+
+       size_t                     out_length = tc->produced;
+       int                        rem_length;
+       int                        dlength;
+
+       assert(out_length != 0);
+       if (((tc->produced + tc->tcfg->inject_ix) > operation->tfrm_op.outlen) || (tc->produced && (operation->tfrm_op.outlen == 0))) {
+               DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));
+               return -EINVAL;
+       }
+       /* Traverse the out iovec until the result inject index is reached. */
+       while ((outiov_ix < operation->tfrm_op.outcount) && ((out_ix + operation->tfrm_op.outdata[outiov_ix].iov_len) <= tc->tcfg->inject_ix)){
+               out_ix += operation->tfrm_op.outdata[outiov_ix].iov_len;
+               outiov_ix++;
+       }
+       if (outiov_ix >= operation->tfrm_op.outcount){
+               DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));
+               return -EINVAL;
+       }
+       iov_offset = tc->tcfg->inject_ix - out_ix;
+       mi.dmasel = tc->unit_no;
+
+       /* Setup the output descriptors. */
+       while ((out_length > 0) && (outiov_ix < operation->tfrm_op.outcount)) {
+               outdesc->next = alloc_cdesc(alloc_flag);
+               if (!outdesc->next) {
+                       DEBUG_API(printk("create_input_descriptors: alloc_cdesc\n"));
+                       err = -ENOMEM;
+                       goto error_cleanup;
+               }
+               outdesc = outdesc->next;
+               rem_length = operation->tfrm_op.outdata[outiov_ix].iov_len - iov_offset;
+               dlength = (out_length < rem_length) ? out_length : rem_length;
+
+               DEBUG(printk("create_input_descriptors:\n"
+                            "outiov_ix=%d, rem_length=%d, dlength=%d\n"
+                            "iov_offset=%d, outdata[outiov_ix].iov_len=%d\n"
+                            "outcount=%d, outiov_ix=%d\n",
+                            outiov_ix, rem_length, dlength, iov_offset, operation->tfrm_op.outdata[outiov_ix].iov_len, operation->tfrm_op.outcount, outiov_ix));
+
+               outdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.outdata[outiov_ix].iov_base + iov_offset);
+               outdesc->dma_descr->after = outdesc->dma_descr->buf + dlength;
+               outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+
+               out_length -= dlength;
+               iov_offset += dlength;
+               if (iov_offset >= operation->tfrm_op.outdata[outiov_ix].iov_len) {
+                       iov_offset = 0;
+                       ++outiov_ix;
+               }
+       }
+       if (out_length > 0){
+               DEBUG_API(printk("create_input_descriptors: not enough room for output, %d remained\n", out_length));
+               err = -EINVAL;
+               goto error_cleanup;
+       }
+       /* Set sync in last descriptor. */
+       mi.sync = 1;
+       outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+
+       *id = head.next;
+       return 0;
+
+ error_cleanup:
+       while (head.next) {
+               outdesc = head.next->next;
+               free_cdesc(head.next);
+               head.next = outdesc;
+       }
+       return err;
+}
+
+
+static int create_output_descriptors(struct cryptocop_operation *operation, int *iniov_ix, int *iniov_offset, size_t desc_len, struct cryptocop_dma_desc **current_out_cdesc, struct strcop_meta_out *meta_out, int alloc_flag)
+{
+       while (desc_len != 0) {
+               struct cryptocop_dma_desc  *cdesc;
+               int                        rem_length = operation->tfrm_op.indata[*iniov_ix].iov_len - *iniov_offset;
+               int                        dlength = (desc_len < rem_length) ? desc_len : rem_length;
+
+               cdesc = alloc_cdesc(alloc_flag);
+               if (!cdesc) {
+                       DEBUG_API(printk("create_output_descriptors: alloc_cdesc\n"));
+                       return -ENOMEM;
+               }
+               (*current_out_cdesc)->next = cdesc;
+               (*current_out_cdesc) = cdesc;
+
+               cdesc->free_buf = NULL;
+
+               cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset);
+               cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength;
+
+               desc_len -= dlength;
+               *iniov_offset += dlength;
+               assert(desc_len >= 0);
+               if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) {
+                       *iniov_offset = 0;
+                       ++(*iniov_ix);
+                       if (*iniov_ix > operation->tfrm_op.incount) {
+                               DEBUG_API(printk("create_output_descriptors: not enough indata in operation."));
+                               return  -EINVAL;
+                       }
+               }
+               cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, (*meta_out));
+       } /* while (desc_len != 0) */
+       /* Last DMA descriptor gets a 'wait' bit to signal expected change in metadata. */
+       (*current_out_cdesc)->dma_descr->wait = 1; /* This will set extraneous WAIT in some situations, e.g. when padding hashes and checksums. */
+
+       return 0;
+}
+
+
+static int append_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_dma_desc **current_in_cdesc, struct cryptocop_dma_desc **current_out_cdesc, struct cryptocop_tfrm_ctx *tc, int alloc_flag)
+{
+       DEBUG(printk("append_input_descriptors, tc=0x%p, unit_no=%d\n", tc, tc->unit_no));
+       if (tc->tcfg) {
+               int                        failed = 0;
+               struct cryptocop_dma_desc  *idescs = NULL;
+               DEBUG(printk("append_input_descriptors: pushing output, consumed %d produced %d bytes.\n", tc->consumed, tc->produced));
+               if (tc->pad_descs) {
+                       DEBUG(printk("append_input_descriptors: append pad descriptors to DMA out list.\n"));
+                       while (tc->pad_descs) {
+                               DEBUG(printk("append descriptor 0x%p\n", tc->pad_descs));
+                               (*current_out_cdesc)->next = tc->pad_descs;
+                               tc->pad_descs = tc->pad_descs->next;
+                               (*current_out_cdesc) = (*current_out_cdesc)->next;
+                       }
+               }
+
+               /* Setup and append output descriptors to DMA in list. */
+               if (tc->unit_no == src_dma){
+                       /* mem2mem.  Setup DMA in descriptors to discard all input prior to the requested mem2mem data. */
+                       struct strcop_meta_in mi = {.sync = 0, .dmasel = src_dma};
+                       unsigned int start_ix = tc->start_ix;
+                       while (start_ix){
+                               unsigned int desclen = start_ix < MEM2MEM_DISCARD_BUF_LENGTH ? start_ix : MEM2MEM_DISCARD_BUF_LENGTH;
+                               (*current_in_cdesc)->next = alloc_cdesc(alloc_flag);
+                               if (!(*current_in_cdesc)->next){
+                                       DEBUG_API(printk("append_input_descriptors: alloc_cdesc mem2mem discard failed\n"));
+                                       return -ENOMEM;
+                               }
+                               (*current_in_cdesc) = (*current_in_cdesc)->next;
+                               (*current_in_cdesc)->dma_descr->buf = (char*)virt_to_phys(mem2mem_discard_buf);
+                               (*current_in_cdesc)->dma_descr->after = (*current_in_cdesc)->dma_descr->buf + desclen;
+                               (*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+                               start_ix -= desclen;
+                       }
+                       mi.sync = 1;
+                       (*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+               }
+
+               failed = create_input_descriptors(operation, tc, &idescs, alloc_flag);
+               if (failed){
+                       DEBUG_API(printk("append_input_descriptors: output descriptor setup failed\n"));
+                       return failed;
+               }
+               DEBUG(printk("append_input_descriptors: append output descriptors to DMA in list.\n"));
+               while (idescs) {
+                       DEBUG(printk("append descriptor 0x%p\n", idescs));
+                       (*current_in_cdesc)->next = idescs;
+                       idescs = idescs->next;
+                       (*current_in_cdesc) = (*current_in_cdesc)->next;
+               }
+       }
+       return 0;
+}
+
+
+
+static int cryptocop_setup_dma_list(struct cryptocop_operation *operation, struct cryptocop_int_operation **int_op, int alloc_flag)
+{
+       struct cryptocop_session *sess;
+       struct cryptocop_transform_ctx *tctx;
+
+       struct cryptocop_tfrm_ctx digest_ctx = {
+               .previous_src = src_none,
+               .current_src = src_none,
+               .start_ix = 0,
+               .requires_padding = 1,
+               .strict_block_length = 0,
+               .hash_conf = 0,
+               .hash_mode = 0,
+               .ciph_conf = 0,
+               .cbcmode = 0,
+               .decrypt = 0,
+               .consumed = 0,
+               .produced = 0,
+               .pad_descs = NULL,
+               .active = 0,
+               .done = 0,
+               .prev_src = NULL,
+               .curr_src = NULL,
+               .tcfg = NULL};
+       struct cryptocop_tfrm_ctx cipher_ctx = {
+               .previous_src = src_none,
+               .current_src = src_none,
+               .start_ix = 0,
+               .requires_padding = 0,
+               .strict_block_length = 1,
+               .hash_conf = 0,
+               .hash_mode = 0,
+               .ciph_conf = 0,
+               .cbcmode = 0,
+               .decrypt = 0,
+               .consumed = 0,
+               .produced = 0,
+               .pad_descs = NULL,
+               .active = 0,
+               .done = 0,
+               .prev_src = NULL,
+               .curr_src = NULL,
+               .tcfg = NULL};
+       struct cryptocop_tfrm_ctx csum_ctx = {
+               .previous_src = src_none,
+               .current_src = src_none,
+               .start_ix = 0,
+               .blocklength = 2,
+               .requires_padding = 1,
+               .strict_block_length = 0,
+               .hash_conf = 0,
+               .hash_mode = 0,
+               .ciph_conf = 0,
+               .cbcmode = 0,
+               .decrypt = 0,
+               .consumed = 0,
+               .produced = 0,
+               .pad_descs = NULL,
+               .active = 0,
+               .done = 0,
+               .tcfg = NULL,
+               .prev_src = NULL,
+               .curr_src = NULL,
+               .unit_no = src_csum};
+       struct cryptocop_tfrm_cfg *tcfg = operation->tfrm_op.tfrm_cfg;
+
+       unsigned int indata_ix = 0;
+
+       /* iovec accounting. */
+       int iniov_ix = 0;
+       int iniov_offset = 0;
+
+       /* Operation descriptor cfg traversal pointer. */
+       struct cryptocop_desc *odsc;
+
+       int failed = 0;
+       /* List heads for allocated descriptors. */
+       struct cryptocop_dma_desc out_cdesc_head = {0};
+       struct cryptocop_dma_desc in_cdesc_head = {0};
+
+       struct cryptocop_dma_desc *current_out_cdesc = &out_cdesc_head;
+       struct cryptocop_dma_desc *current_in_cdesc = &in_cdesc_head;
+
+       struct cryptocop_tfrm_ctx *output_tc = NULL;
+       void                      *iop_alloc_ptr;
+
+       assert(operation != NULL);
+       assert(int_op != NULL);
+
+       DEBUG(printk("cryptocop_setup_dma_list: start\n"));
+       DEBUG(print_cryptocop_operation(operation));
+
+       sess = get_session(operation->sid);
+       if (!sess) {
+               DEBUG_API(printk("cryptocop_setup_dma_list: no session found for operation.\n"));
+               failed = -EINVAL;
+               goto error_cleanup;
+       }
+       iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);
+       if (!iop_alloc_ptr) {
+               DEBUG_API(printk("cryptocop_setup_dma_list:  kmalloc cryptocop_int_operation\n"));
+               failed = -ENOMEM;
+               goto error_cleanup;
+       }
+       (*int_op) = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));
+       DEBUG(memset((*int_op), 0xff, sizeof(struct cryptocop_int_operation)));
+       (*int_op)->alloc_ptr = iop_alloc_ptr;
+       DEBUG(printk("cryptocop_setup_dma_list: *int_op=0x%p, alloc_ptr=0x%p\n", *int_op, (*int_op)->alloc_ptr));
+
+       (*int_op)->sid = operation->sid;
+       (*int_op)->cdesc_out = NULL;
+       (*int_op)->cdesc_in = NULL;
+       (*int_op)->tdes_mode = cryptocop_3des_ede;
+       (*int_op)->csum_mode = cryptocop_csum_le;
+       (*int_op)->ddesc_out = NULL;
+       (*int_op)->ddesc_in = NULL;
+
+       /* Scan operation->tfrm_op.tfrm_cfg for bad configuration and set up the local contexts. */
+       if (!tcfg) {
+               DEBUG_API(printk("cryptocop_setup_dma_list: no configured transforms in operation.\n"));
+               failed = -EINVAL;
+               goto error_cleanup;
+       }
+       while (tcfg) {
+               tctx = get_transform_ctx(sess, tcfg->tid);
+               if (!tctx) {
+                       DEBUG_API(printk("cryptocop_setup_dma_list: no transform id %d in session.\n", tcfg->tid));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+               if (tcfg->inject_ix > operation->tfrm_op.outlen){
+                       DEBUG_API(printk("cryptocop_setup_dma_list: transform id %d inject_ix (%d) > operation->tfrm_op.outlen(%d)", tcfg->tid, tcfg->inject_ix, operation->tfrm_op.outlen));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+               switch (tctx->init.alg){
+               case cryptocop_alg_mem2mem:
+                       if (cipher_ctx.tcfg != NULL){
+                               DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       /* mem2mem is handled as a NULL cipher. */
+                       cipher_ctx.cbcmode = 0;
+                       cipher_ctx.decrypt = 0;
+                       cipher_ctx.blocklength = 1;
+                       cipher_ctx.ciph_conf = 0;
+                       cipher_ctx.unit_no = src_dma;
+                       cipher_ctx.tcfg = tcfg;
+                       cipher_ctx.tctx = tctx;
+                       break;
+               case cryptocop_alg_des:
+               case cryptocop_alg_3des:
+               case cryptocop_alg_aes:
+                       /* cipher */
+                       if (cipher_ctx.tcfg != NULL){
+                               DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       cipher_ctx.tcfg = tcfg;
+                       cipher_ctx.tctx = tctx;
+                       if (cipher_ctx.tcfg->flags & CRYPTOCOP_DECRYPT){
+                               cipher_ctx.decrypt = 1;
+                       }
+                       switch (tctx->init.cipher_mode) {
+                       case cryptocop_cipher_mode_ecb:
+                               cipher_ctx.cbcmode = 0;
+                               break;
+                       case cryptocop_cipher_mode_cbc:
+                               cipher_ctx.cbcmode = 1;
+                               break;
+                       default:
+                               DEBUG_API(printk("cryptocop_setup_dma_list: cipher_ctx, bad cipher mode==%d\n", tctx->init.cipher_mode));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx, set CBC mode==%d\n", cipher_ctx.cbcmode));
+                       switch (tctx->init.alg){
+                       case cryptocop_alg_des:
+                               cipher_ctx.ciph_conf = 0;
+                               cipher_ctx.unit_no = src_des;
+                               cipher_ctx.blocklength = DES_BLOCK_LENGTH;
+                               break;
+                       case cryptocop_alg_3des:
+                               cipher_ctx.ciph_conf = 1;
+                               cipher_ctx.unit_no = src_des;
+                               cipher_ctx.blocklength = DES_BLOCK_LENGTH;
+                               break;
+                       case cryptocop_alg_aes:
+                               cipher_ctx.ciph_conf = 2;
+                               cipher_ctx.unit_no = src_aes;
+                               cipher_ctx.blocklength = AES_BLOCK_LENGTH;
+                               break;
+                       default:
+                               panic("cryptocop_setup_dma_list: impossible algorithm %d\n", tctx->init.alg);
+                       }
+                       (*int_op)->tdes_mode = tctx->init.tdes_mode;
+                       break;
+               case cryptocop_alg_md5:
+               case cryptocop_alg_sha1:
+                       /* digest */
+                       if (digest_ctx.tcfg != NULL){
+                               DEBUG_API(printk("cryptocop_setup_dma_list: multiple digests in operation.\n"));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       digest_ctx.tcfg = tcfg;
+                       digest_ctx.tctx = tctx;
+                       digest_ctx.hash_mode = 0; /* Don't use explicit IV in this API. */
+                       switch (tctx->init.alg){
+                       case cryptocop_alg_md5:
+                               digest_ctx.blocklength = MD5_BLOCK_LENGTH;
+                               digest_ctx.unit_no = src_md5;
+                               digest_ctx.hash_conf = 1; /* 1 => MD-5 */
+                               break;
+                       case cryptocop_alg_sha1:
+                               digest_ctx.blocklength = SHA1_BLOCK_LENGTH;
+                               digest_ctx.unit_no = src_sha1;
+                               digest_ctx.hash_conf = 0; /* 0 => SHA-1 */
+                               break;
+                       default:
+                               panic("cryptocop_setup_dma_list: impossible digest algorithm\n");
+                       }
+                       break;
+               case cryptocop_alg_csum:
+                       /* digest */
+                       if (csum_ctx.tcfg != NULL){
+                               DEBUG_API(printk("cryptocop_setup_dma_list: multiple checksums in operation.\n"));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       (*int_op)->csum_mode = tctx->init.csum_mode;
+                       csum_ctx.tcfg = tcfg;
+                       csum_ctx.tctx = tctx;
+                       break;
+               default:
+                       /* no algorithm. */
+                       DEBUG_API(printk("cryptocop_setup_dma_list: invalid algorithm %d specified in tfrm %d.\n", tctx->init.alg, tcfg->tid));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+               tcfg = tcfg->next;
+       }
+       /* Download key if a cipher is used. */
+       if (cipher_ctx.tcfg && (cipher_ctx.tctx->init.alg != cryptocop_alg_mem2mem)){
+               struct cryptocop_dma_desc  *key_desc = NULL;
+
+               failed = setup_key_dl_desc(&cipher_ctx, &key_desc, alloc_flag);
+               if (failed) {
+                       DEBUG_API(printk("cryptocop_setup_dma_list: setup key dl\n"));
+                       goto error_cleanup;
+               }
+               current_out_cdesc->next = key_desc;
+               current_out_cdesc = key_desc;
+               indata_ix += (unsigned int)(key_desc->dma_descr->after - key_desc->dma_descr->buf);
+
+               /* Download explicit IV if a cipher is used and CBC mode and explicit IV selected. */
+               if ((cipher_ctx.tctx->init.cipher_mode == cryptocop_cipher_mode_cbc) && (cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV)) {
+                       struct cryptocop_dma_desc  *iv_desc = NULL;
+
+                       DEBUG(printk("cryptocop_setup_dma_list: setup cipher CBC IV descriptor.\n"));
+
+                       failed = setup_cipher_iv_desc(&cipher_ctx, &iv_desc, alloc_flag);
+                       if (failed) {
+                               DEBUG_API(printk("cryptocop_setup_dma_list: CBC IV descriptor.\n"));
+                               goto error_cleanup;
+                       }
+                       current_out_cdesc->next = iv_desc;
+                       current_out_cdesc = iv_desc;
+                       indata_ix += (unsigned int)(iv_desc->dma_descr->after - iv_desc->dma_descr->buf);
+               }
+       }
+
+       /* Process descriptors. */
+       odsc = operation->tfrm_op.desc;
+       while (odsc) {
+               struct cryptocop_desc_cfg   *dcfg = odsc->cfg;
+               struct strcop_meta_out      meta_out = {0};
+               size_t                      desc_len = odsc->length;
+               int                         active_count, eop_needed_count;
+
+               output_tc = NULL;
+
+               DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor\n"));
+
+               while (dcfg) {
+                       struct cryptocop_tfrm_ctx  *tc = NULL;
+
+                       DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor configuration.\n"));
+                       /* Get the local context for the transform and mark it as the output unit if it produces output. */
+                       if (digest_ctx.tcfg && (digest_ctx.tcfg->tid == dcfg->tid)){
+                               tc = &digest_ctx;
+                       } else if (cipher_ctx.tcfg && (cipher_ctx.tcfg->tid == dcfg->tid)){
+                               tc = &cipher_ctx;
+                       } else if (csum_ctx.tcfg && (csum_ctx.tcfg->tid == dcfg->tid)){
+                               tc = &csum_ctx;
+                       }
+                       if (!tc) {
+                               DEBUG_API(printk("cryptocop_setup_dma_list: invalid transform %d specified in descriptor.\n", dcfg->tid));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       if (tc->done) {
+                               DEBUG_API(printk("cryptocop_setup_dma_list: completed transform %d reused.\n", dcfg->tid));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       if (!tc->active) {
+                               tc->start_ix = indata_ix;
+                               tc->active = 1;
+                       }
+
+                       tc->previous_src = tc->current_src;
+                       tc->prev_src = tc->curr_src;
+                       /* Map source unit id to DMA source config. */
+                       switch (dcfg->src){
+                       case cryptocop_source_dma:
+                               tc->current_src = src_dma;
+                               break;
+                       case cryptocop_source_des:
+                               tc->current_src = src_des;
+                               break;
+                       case cryptocop_source_3des:
+                               tc->current_src = src_des;
+                               break;
+                       case cryptocop_source_aes:
+                               tc->current_src = src_aes;
+                               break;
+                       case cryptocop_source_md5:
+                       case cryptocop_source_sha1:
+                       case cryptocop_source_csum:
+                       case cryptocop_source_none:
+                       default:
+                               /* We do not allow using accumulating style units (SHA-1, MD5, checksum) as sources to other units.
+                                */
+                               DEBUG_API(printk("cryptocop_setup_dma_list: bad unit source configured %d.\n", dcfg->src));
+                               failed = -EINVAL;
+                               goto error_cleanup;
+                       }
+                       if (tc->current_src != src_dma) {
+                               /* Find the unit we are sourcing from. */
+                               if (digest_ctx.unit_no == tc->current_src){
+                                       tc->curr_src = &digest_ctx;
+                               } else if (cipher_ctx.unit_no == tc->current_src){
+                                       tc->curr_src = &cipher_ctx;
+                               } else if (csum_ctx.unit_no == tc->current_src){
+                                       tc->curr_src = &csum_ctx;
+                               }
+                               if ((tc->curr_src == tc) && (tc->unit_no != src_dma)){
+                                       DEBUG_API(printk("cryptocop_setup_dma_list: unit %d configured to source from itself.\n", tc->unit_no));
+                                       failed = -EINVAL;
+                                       goto error_cleanup;
+                               }
+                       } else {
+                               tc->curr_src = NULL;
+                       }
+
+                       /* Detect source switch. */
+                       DEBUG(printk("cryptocop_setup_dma_list: tc->active=%d tc->unit_no=%d tc->current_src=%d tc->previous_src=%d, tc->curr_src=0x%p, tc->prev_srv=0x%p\n", tc->active, tc->unit_no, tc->current_src, tc->previous_src, tc->curr_src, tc->prev_src));
+                       if (tc->active && (tc->current_src != tc->previous_src)) {
+                               /* Only allow source switch when both the old source unit and the new one have
+                                * no pending data to process (i.e. the consumed length must be a multiple of the
+                                * transform blocklength). */
+                               /* Note: if the src == NULL we are actually sourcing from DMA out. */
+                               if (((tc->prev_src != NULL) && (tc->prev_src->consumed % tc->prev_src->blocklength)) ||
+                                   ((tc->curr_src != NULL) && (tc->curr_src->consumed % tc->curr_src->blocklength)))
+                               {
+                                       DEBUG_API(printk("cryptocop_setup_dma_list: can only disconnect from or connect to a unit on a multiple of the blocklength, old: cons=%d, prod=%d, block=%d, new: cons=%d prod=%d, block=%d.\n", tc->prev_src ? tc->prev_src->consumed : INT_MIN, tc->prev_src ? tc->prev_src->produced : INT_MIN, tc->prev_src ? tc->prev_src->blocklength : INT_MIN, tc->curr_src ? tc->curr_src->consumed : INT_MIN, tc->curr_src ? tc->curr_src->produced : INT_MIN, tc->curr_src ? tc->curr_src->blocklength : INT_MIN));
+                                       failed = -EINVAL;
+                                       goto error_cleanup;
+                               }
+                       }
+                       /* Detect unit deactivation. */
+                       if (dcfg->last) {
+                               /* Length check of this is handled below. */
+                               tc->done = 1;
+                       }
+                       dcfg = dcfg->next;
+               } /* while (dcfg) */
+               DEBUG(printk("cryptocop_setup_dma_list: parsing operation descriptor configuration complete.\n"));
+
+               if (cipher_ctx.active && (cipher_ctx.curr_src != NULL) && !cipher_ctx.curr_src->active){
+                       DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", cipher_ctx.curr_src->unit_no));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+               if (digest_ctx.active && (digest_ctx.curr_src != NULL) && !digest_ctx.curr_src->active){
+                       DEBUG_API(printk("cryptocop_setup_dma_list: digest source from inactive unit %d\n", digest_ctx.curr_src->unit_no));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+               if (csum_ctx.active && (csum_ctx.curr_src != NULL) && !csum_ctx.curr_src->active){
+                       DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", csum_ctx.curr_src->unit_no));
+                       failed = -EINVAL;
+                       goto error_cleanup;
+               }
+
+               /* Update consumed and produced lengths.
+
+                  The consumed length accounting here is actually cheating.  If a unit source from DMA (or any
+                  other unit that process data in blocks of one octet) it is correct, but if it source from a
+                  block processing unit, i.e. a cipher, it will be temporarily incorrect at some times.  However
+                  since it is only allowed--by the HW--to change source to or from a block processing unit at times where that
+                  unit has processed an exact multiple of its block length the end result will be correct.
+                  Beware that if the source change restriction change this code will need to be (much) reworked.
+               */
+               DEBUG(printk("cryptocop_setup_dma_list: desc->length=%d, desc_len=%d.\n", odsc->length, desc_len));
+
+               if (csum_ctx.active) {
+                       csum_ctx.consumed += desc_len;
+                       if (csum_ctx.done) {
+                               csum_ctx.produced = 2;
+                       }
+                       DEBUG(printk("cryptocop_setup_dma_list: csum_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", csum_ctx.consumed, csum_ctx.produced, csum_ctx.blocklength));
+               }
+               if (digest_ctx.active) {
+                       digest_ctx.consumed += desc_len;
+                       if (digest_ctx.done) {
+                               if (digest_ctx.unit_no == src_md5) {
+                                       digest_ctx.produced = MD5_STATE_LENGTH;
+                               } else {
+                                       digest_ctx.produced = SHA1_STATE_LENGTH;
+                               }
+                       }
+                       DEBUG(printk("cryptocop_setup_dma_list: digest_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", digest_ctx.consumed, digest_ctx.produced, digest_ctx.blocklength));
+               }
+               if (cipher_ctx.active) {
+                       /* Ciphers are allowed only to source from DMA out.  That is filtered above. */
+                       assert(cipher_ctx.current_src == src_dma);
+                       cipher_ctx.consumed += desc_len;
+                       cipher_ctx.produced = cipher_ctx.blocklength * (cipher_ctx.consumed / cipher_ctx.blocklength);
+                       if (cipher_ctx.cbcmode && !(cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV) && cipher_ctx.produced){
+                               cipher_ctx.produced -= cipher_ctx.blocklength; /* Compensate for CBC iv. */
+                       }
+                       DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", cipher_ctx.consumed, cipher_ctx.produced, cipher_ctx.blocklength));
+               }
+
+               /* Setup the DMA out descriptors. */
+               /* Configure the metadata. */
+               active_count = 0;
+               eop_needed_count = 0;
+               if (cipher_ctx.active) {
+                       ++active_count;
+                       if (cipher_ctx.unit_no == src_dma){
+                               /* mem2mem */
+                               meta_out.ciphsel = src_none;
+                       } else {
+                               meta_out.ciphsel = cipher_ctx.current_src;
+                       }
+                       meta_out.ciphconf = cipher_ctx.ciph_conf;
+                       meta_out.cbcmode = cipher_ctx.cbcmode;
+                       meta_out.decrypt = cipher_ctx.decrypt;
+                       DEBUG(printk("set ciphsel=%d ciphconf=%d cbcmode=%d decrypt=%d\n", meta_out.ciphsel, meta_out.ciphconf, meta_out.cbcmode, meta_out.decrypt));
+                       if (cipher_ctx.done) ++eop_needed_count;
+               } else {
+                       meta_out.ciphsel = src_none;
+               }
+
+               if (digest_ctx.active) {
+                       ++active_count;
+                       meta_out.hashsel = digest_ctx.current_src;
+                       meta_out.hashconf = digest_ctx.hash_conf;
+                       meta_out.hashmode = 0; /* Explicit mode is not used here. */
+                       DEBUG(printk("set hashsel=%d hashconf=%d hashmode=%d\n", meta_out.hashsel, meta_out.hashconf, meta_out.hashmode));
+                       if (digest_ctx.done) {
+                               assert(digest_ctx.pad_descs == NULL);
+                               failed = create_pad_descriptor(&digest_ctx, &digest_ctx.pad_descs, alloc_flag);
+                               if (failed) {
+                                       DEBUG_API(printk("cryptocop_setup_dma_list: failed digest pad creation.\n"));
+                                       goto error_cleanup;
+                               }
+                       }
+               } else {
+                       meta_out.hashsel = src_none;
+               }
+
+               if (csum_ctx.active) {
+                       ++active_count;
+                       meta_out.csumsel = csum_ctx.current_src;
+                       if (csum_ctx.done) {
+                               assert(csum_ctx.pad_descs == NULL);
+                               failed = create_pad_descriptor(&csum_ctx, &csum_ctx.pad_descs, alloc_flag);
+                               if (failed) {
+                                       DEBUG_API(printk("cryptocop_setup_dma_list: failed csum pad creation.\n"));
+                                       goto error_cleanup;
+                               }
+                       }
+               } else {
+                       meta_out.csumsel = src_none;
+               }
+               DEBUG(printk("cryptocop_setup_dma_list: %d eop needed, %d active units\n", eop_needed_count, active_count));
+               /* Setup DMA out descriptors for the indata. */
+               failed = create_output_descriptors(operation, &iniov_ix, &iniov_offset, desc_len, &current_out_cdesc, &meta_out, alloc_flag);
+               if (failed) {
+                       DEBUG_API(printk("cryptocop_setup_dma_list: create_output_descriptors %d\n", failed));
+                       goto error_cleanup;
+               }
+               /* Setup out EOP.  If there are active units that are not done here they cannot get an EOP
+                * so we ust setup a zero length descriptor to DMA to signal EOP only to done units.
+                * If there is a pad descriptor EOP for the padded unit will be EOPed by it.
+                */
+               assert(active_count >= eop_needed_count);
+               assert((eop_needed_count == 0) || (eop_needed_count == 1));
+               if (eop_needed_count) {
+                       /* This means that the bulk operation (cipeher/m2m) is terminated. */
+                       if (active_count > 1) {
+                               /* Use zero length EOP descriptor. */
+                               struct cryptocop_dma_desc *ed = alloc_cdesc(alloc_flag);
+                               struct strcop_meta_out    ed_mo = {0};
+                               if (!ed) {
+                                       DEBUG_API(printk("cryptocop_setup_dma_list: alloc EOP descriptor for cipher\n"));
+                                       failed = -ENOMEM;
+                                       goto error_cleanup;
+                               }
+
+                               assert(cipher_ctx.active && cipher_ctx.done);
+
+                               if (cipher_ctx.unit_no == src_dma){
+                                       /* mem2mem */
+                                       ed_mo.ciphsel = src_none;
+                               } else {
+                                       ed_mo.ciphsel = cipher_ctx.current_src;
+                               }
+                               ed_mo.ciphconf = cipher_ctx.ciph_conf;
+                               ed_mo.cbcmode = cipher_ctx.cbcmode;
+                               ed_mo.decrypt = cipher_ctx.decrypt;
+
+                               ed->free_buf = NULL;
+                               ed->dma_descr->wait = 1;
+                               ed->dma_descr->out_eop = 1;
+
+                               ed->dma_descr->buf = (char*)virt_to_phys(&ed); /* Use any valid physical address for zero length descriptor. */
+                               ed->dma_descr->after = ed->dma_descr->buf;
+                               ed->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, ed_mo);
+                               current_out_cdesc->next = ed;
+                               current_out_cdesc = ed;
+                       } else {
+                               /* Set EOP in the current out descriptor since the only active module is
+                                * the one needing the EOP. */
+
+                               current_out_cdesc->dma_descr->out_eop = 1;
+                       }
+               }
+
+               if (cipher_ctx.done && cipher_ctx.active) cipher_ctx.active = 0;
+               if (digest_ctx.done && digest_ctx.active) digest_ctx.active = 0;
+               if (csum_ctx.done && csum_ctx.active) csum_ctx.active = 0;
+               indata_ix += odsc->length;
+               odsc = odsc->next;
+       } /* while (odsc) */ /* Process descriptors. */
+       DEBUG(printk("cryptocop_setup_dma_list: done parsing operation descriptors\n"));
+       if (cipher_ctx.tcfg && (cipher_ctx.active || !cipher_ctx.done)){
+               DEBUG_API(printk("cryptocop_setup_dma_list: cipher operation not terminated.\n"));
+               failed = -EINVAL;
+               goto error_cleanup;
+       }
+       if (digest_ctx.tcfg && (digest_ctx.active || !digest_ctx.done)){
+               DEBUG_API(printk("cryptocop_setup_dma_list: digest operation not terminated.\n"));
+               failed = -EINVAL;
+               goto error_cleanup;
+       }
+       if (csum_ctx.tcfg && (csum_ctx.active || !csum_ctx.done)){
+               DEBUG_API(printk("cryptocop_setup_dma_list: csum operation not terminated.\n"));
+               failed = -EINVAL;
+               goto error_cleanup;
+       }
+
+       failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &cipher_ctx, alloc_flag);
+       if (failed){
+               DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+               goto error_cleanup;
+       }
+       failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &digest_ctx, alloc_flag);
+       if (failed){
+               DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+               goto error_cleanup;
+       }
+       failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &csum_ctx, alloc_flag);
+       if (failed){
+               DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+               goto error_cleanup;
+       }
+
+       DEBUG(printk("cryptocop_setup_dma_list: int_op=0x%p, *int_op=0x%p\n", int_op, *int_op));
+       (*int_op)->cdesc_out = out_cdesc_head.next;
+       (*int_op)->cdesc_in = in_cdesc_head.next;
+       DEBUG(printk("cryptocop_setup_dma_list: out_cdesc_head=0x%p in_cdesc_head=0x%p\n", (*int_op)->cdesc_out, (*int_op)->cdesc_in));
+
+       setup_descr_chain(out_cdesc_head.next);
+       setup_descr_chain(in_cdesc_head.next);
+
+       /* Last but not least: mark the last DMA in descriptor for a INTR and EOL and the the
+        * last DMA out descriptor for EOL.
+        */
+       current_in_cdesc->dma_descr->intr = 1;
+       current_in_cdesc->dma_descr->eol = 1;
+       current_out_cdesc->dma_descr->eol = 1;
+
+       /* Setup DMA contexts. */
+       (*int_op)->ctx_out.next = NULL;
+       (*int_op)->ctx_out.eol = 1;
+       (*int_op)->ctx_out.intr = 0;
+       (*int_op)->ctx_out.store_mode = 0;
+       (*int_op)->ctx_out.en = 0;
+       (*int_op)->ctx_out.dis = 0;
+       (*int_op)->ctx_out.md0 = 0;
+       (*int_op)->ctx_out.md1 = 0;
+       (*int_op)->ctx_out.md2 = 0;
+       (*int_op)->ctx_out.md3 = 0;
+       (*int_op)->ctx_out.md4 = 0;
+       (*int_op)->ctx_out.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_out->dma_descr);
+       (*int_op)->ctx_out.saved_data_buf = (*int_op)->cdesc_out->dma_descr->buf; /* Already physical address. */
+
+       (*int_op)->ctx_in.next = NULL;
+       (*int_op)->ctx_in.eol = 1;
+       (*int_op)->ctx_in.intr = 0;
+       (*int_op)->ctx_in.store_mode = 0;
+       (*int_op)->ctx_in.en = 0;
+       (*int_op)->ctx_in.dis = 0;
+       (*int_op)->ctx_in.md0 = 0;
+       (*int_op)->ctx_in.md1 = 0;
+       (*int_op)->ctx_in.md2 = 0;
+       (*int_op)->ctx_in.md3 = 0;
+       (*int_op)->ctx_in.md4 = 0;
+
+       (*int_op)->ctx_in.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_in->dma_descr);
+       (*int_op)->ctx_in.saved_data_buf = (*int_op)->cdesc_in->dma_descr->buf; /* Already physical address. */
+
+       DEBUG(printk("cryptocop_setup_dma_list: done\n"));
+       return 0;
+
+error_cleanup:
+       {
+               /* Free all allocated resources. */
+               struct cryptocop_dma_desc *tmp_cdesc;
+               while (digest_ctx.pad_descs){
+                       tmp_cdesc = digest_ctx.pad_descs->next;
+                       free_cdesc(digest_ctx.pad_descs);
+                       digest_ctx.pad_descs = tmp_cdesc;
+               }
+               while (csum_ctx.pad_descs){
+                       tmp_cdesc = csum_ctx.pad_descs->next;
+                       free_cdesc(csum_ctx.pad_descs);
+                       csum_ctx.pad_descs = tmp_cdesc;
+               }
+               assert(cipher_ctx.pad_descs == NULL); /* The ciphers are never padded. */
+
+               if (*int_op != NULL) delete_internal_operation(*int_op);
+       }
+       DEBUG_API(printk("cryptocop_setup_dma_list: done with error %d\n", failed));
+       return failed;
+}
+
+
+static void delete_internal_operation(struct cryptocop_int_operation *iop)
+{
+       void                      *ptr = iop->alloc_ptr;
+       struct cryptocop_dma_desc *cd = iop->cdesc_out;
+       struct cryptocop_dma_desc *next;
+
+       DEBUG(printk("delete_internal_operation: iop=0x%p, alloc_ptr=0x%p\n", iop, ptr));
+
+       while (cd) {
+               next = cd->next;
+               free_cdesc(cd);
+               cd = next;
+       }
+       cd = iop->cdesc_in;
+       while (cd) {
+               next = cd->next;
+               free_cdesc(cd);
+               cd = next;
+       }
+       kfree(ptr);
+}
+
+#define MD5_MIN_PAD_LENGTH (9)
+#define MD5_PAD_LENGTH_FIELD_LENGTH (8)
+
+static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)
+{
+       size_t                  padlen = MD5_BLOCK_LENGTH - (hashed_length % MD5_BLOCK_LENGTH);
+       unsigned char           *p;
+       int                     i;
+       unsigned long long int  bit_length = hashed_length << 3;
+
+       if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH;
+
+       p = kmalloc(padlen, alloc_flag);
+       if (!pad) return -ENOMEM;
+
+       *p = 0x80;
+       memset(p+1, 0, padlen - 1);
+
+       DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
+
+       i = padlen - MD5_PAD_LENGTH_FIELD_LENGTH;
+       while (bit_length != 0){
+               p[i++] = bit_length % 0x100;
+               bit_length >>= 8;
+       }
+
+       *pad = (char*)p;
+       *pad_length = padlen;
+
+       return 0;
+}
+
+#define SHA1_MIN_PAD_LENGTH (9)
+#define SHA1_PAD_LENGTH_FIELD_LENGTH (8)
+
+static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)
+{
+       size_t                  padlen = SHA1_BLOCK_LENGTH - (hashed_length % SHA1_BLOCK_LENGTH);
+       unsigned char           *p;
+       int                     i;
+       unsigned long long int  bit_length = hashed_length << 3;
+
+       if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH;
+
+       p = kmalloc(padlen, alloc_flag);
+       if (!pad) return -ENOMEM;
+
+       *p = 0x80;
+       memset(p+1, 0, padlen - 1);
+
+       DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
+
+       i = padlen - 1;
+       while (bit_length != 0){
+               p[i--] = bit_length % 0x100;
+               bit_length >>= 8;
+       }
+
+       *pad = (char*)p;
+       *pad_length = padlen;
+
+       return 0;
+}
+
+
+static int transform_ok(struct cryptocop_transform_init *tinit)
+{
+       switch (tinit->alg){
+       case cryptocop_alg_csum:
+               switch (tinit->csum_mode){
+               case cryptocop_csum_le:
+               case cryptocop_csum_be:
+                       break;
+               default:
+                       DEBUG_API(printk("transform_ok: Bad mode set for csum transform\n"));
+                       return -EINVAL;
+               }
+       case cryptocop_alg_mem2mem:
+       case cryptocop_alg_md5:
+       case cryptocop_alg_sha1:
+               if (tinit->keylen != 0) {
+                       DEBUG_API(printk("transform_ok: non-zero keylength, %d, for a digest/csum algorithm\n", tinit->keylen));
+                       return -EINVAL; /* This check is a bit strict. */
+               }
+               break;
+       case cryptocop_alg_des:
+               if (tinit->keylen != 64) {
+                       DEBUG_API(printk("transform_ok: keylen %d invalid for DES\n", tinit->keylen));
+                       return -EINVAL;
+               }
+               break;
+       case cryptocop_alg_3des:
+               if (tinit->keylen != 192) {
+                       DEBUG_API(printk("transform_ok: keylen %d invalid for 3DES\n", tinit->keylen));
+                       return -EINVAL;
+               }
+               break;
+       case cryptocop_alg_aes:
+               if (tinit->keylen != 128 && tinit->keylen != 192 && tinit->keylen != 256) {
+                       DEBUG_API(printk("transform_ok: keylen %d invalid for AES\n", tinit->keylen));
+                       return -EINVAL;
+               }
+               break;
+       case cryptocop_no_alg:
+       default:
+               DEBUG_API(printk("transform_ok: no such algorithm %d\n", tinit->alg));
+               return -EINVAL;
+       }
+
+       switch (tinit->alg){
+       case cryptocop_alg_des:
+       case cryptocop_alg_3des:
+       case cryptocop_alg_aes:
+               if (tinit->cipher_mode != cryptocop_cipher_mode_ecb && tinit->cipher_mode != cryptocop_cipher_mode_cbc) return -EINVAL;
+       default:
+                break;
+       }
+       return 0;
+}
+
+
+int cryptocop_new_session(cryptocop_session_id *sid, struct cryptocop_transform_init *tinit, int alloc_flag)
+{
+       struct cryptocop_session         *sess;
+       struct cryptocop_transform_init  *tfrm_in = tinit;
+       struct cryptocop_transform_init  *tmp_in;
+       int                              no_tfrms = 0;
+       int                              i;
+       unsigned long int                flags;
+
+       init_stream_coprocessor(); /* For safety if we are called early */
+
+       while (tfrm_in){
+               int err;
+               ++no_tfrms;
+               if ((err = transform_ok(tfrm_in))) {
+                       DEBUG_API(printk("cryptocop_new_session, bad transform\n"));
+                       return err;
+               }
+               tfrm_in = tfrm_in->next;
+       }
+       if (0 == no_tfrms) {
+               DEBUG_API(printk("cryptocop_new_session, no transforms specified\n"));
+               return -EINVAL;
+       }
+
+       sess = kmalloc(sizeof(struct cryptocop_session), alloc_flag);
+       if (!sess){
+               DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_session\n"));
+               return -ENOMEM;
+       }
+
+       sess->tfrm_ctx = kmalloc(no_tfrms * sizeof(struct cryptocop_transform_ctx), alloc_flag);
+       if (!sess->tfrm_ctx) {
+               DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_transform_ctx\n"));
+               kfree(sess);
+               return -ENOMEM;
+       }
+
+       tfrm_in = tinit;
+       for (i = 0; i < no_tfrms; i++){
+               tmp_in = tfrm_in->next;
+               while (tmp_in){
+                       if (tmp_in->tid == tfrm_in->tid) {
+                               DEBUG_API(printk("cryptocop_new_session, duplicate transform ids\n"));
+                               kfree(sess->tfrm_ctx);
+                               kfree(sess);
+                               return -EINVAL;
+                       }
+                       tmp_in = tmp_in->next;
+               }
+               memcpy(&sess->tfrm_ctx[i].init, tfrm_in, sizeof(struct cryptocop_transform_init));
+               sess->tfrm_ctx[i].dec_key_set = 0;
+               sess->tfrm_ctx[i].next = &sess->tfrm_ctx[i] + 1;
+
+               tfrm_in = tfrm_in->next;
+       }
+       sess->tfrm_ctx[i-1].next = NULL;
+
+       spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+       sess->sid = next_sid;
+       next_sid++;
+       /* TODO If we are really paranoid we should do duplicate check to handle sid wraparound.
+        *      OTOH 2^64 is a really large number of session. */
+       if (next_sid == 0) next_sid = 1;
+
+       /* Prepend to session list. */
+       sess->next = cryptocop_sessions;
+       cryptocop_sessions = sess;
+       spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+       *sid = sess->sid;
+       return 0;
+}
+
+
+int cryptocop_free_session(cryptocop_session_id sid)
+{
+       struct cryptocop_transform_ctx    *tc;
+       struct cryptocop_session          *sess = NULL;
+       struct cryptocop_session          *psess = NULL;
+       unsigned long int                 flags;
+       int                               i;
+       LIST_HEAD(remove_list);
+       struct list_head                  *node, *tmp;
+       struct cryptocop_prio_job         *pj;
+
+       DEBUG(printk("cryptocop_free_session: sid=%lld\n", sid));
+
+       spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+       sess = cryptocop_sessions;
+       while (sess && sess->sid != sid){
+               psess = sess;
+               sess = sess->next;
+       }
+       if (sess){
+               if (psess){
+                       psess->next = sess->next;
+               } else {
+                       cryptocop_sessions = sess->next;
+               }
+       }
+       spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+
+       if (!sess) return -EINVAL;
+
+       /* Remove queued jobs. */
+       spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+
+       for (i = 0; i < cryptocop_prio_no_prios; i++){
+               if (!list_empty(&(cryptocop_job_queues[i].jobs))){
+                       list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {
+                               pj = list_entry(node, struct cryptocop_prio_job, node);
+                               if (pj->oper->sid == sid) {
+                                       list_move_tail(node, &remove_list);
+                               }
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+
+       list_for_each_safe(node, tmp, &remove_list) {
+               list_del(node);
+               pj = list_entry(node, struct cryptocop_prio_job, node);
+               pj->oper->operation_status = -EAGAIN;  /* EAGAIN is not ideal for job/session terminated but it's the best choice I know of. */
+               DEBUG(printk("cryptocop_free_session: pj=0x%p, pj->oper=0x%p, pj->iop=0x%p\n", pj, pj->oper, pj->iop));
+               pj->oper->cb(pj->oper, pj->oper->cb_data);
+               delete_internal_operation(pj->iop);
+               kfree(pj);
+       }
+
+       tc = sess->tfrm_ctx;
+       /* Erase keying data. */
+       while (tc){
+               DEBUG(printk("cryptocop_free_session: memset keys, tfrm id=%d\n", tc->init.tid));
+               memset(tc->init.key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);
+               memset(tc->dec_key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);
+               tc = tc->next;
+       }
+       kfree(sess->tfrm_ctx);
+       kfree(sess);
+
+       return 0;
+}
+
+static struct cryptocop_session *get_session(cryptocop_session_id sid)
+{
+       struct cryptocop_session    *sess;
+       unsigned long int           flags;
+
+       spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+       sess = cryptocop_sessions;
+       while (sess && (sess->sid != sid)){
+               sess = sess->next;
+       }
+       spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+
+       return sess;
+}
+
+static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid)
+{
+       struct cryptocop_transform_ctx *tc = sess->tfrm_ctx;
+
+       DEBUG(printk("get_transform_ctx, sess=0x%p, tid=%d\n", sess, tid));
+       assert(sess != NULL);
+       while (tc && tc->init.tid != tid){
+               DEBUG(printk("tc=0x%p, tc->next=0x%p\n", tc, tc->next));
+               tc = tc->next;
+       }
+       DEBUG(printk("get_transform_ctx, returning tc=0x%p\n", tc));
+       return tc;
+}
+
+
+
+/* The AES s-transform matrix (s-box). */
+static const u8 aes_sbox[256] = {
+       99,  124, 119, 123, 242, 107, 111, 197, 48,  1,   103, 43,  254, 215, 171, 118,
+       202, 130, 201, 125, 250, 89,  71,  240, 173, 212, 162, 175, 156, 164, 114, 192,
+       183, 253, 147, 38,  54,  63,  247, 204, 52,  165, 229, 241, 113, 216, 49,  21,
+       4,   199, 35,  195, 24,  150, 5,   154, 7,   18,  128, 226, 235, 39,  178, 117,
+       9,   131, 44,  26,  27,  110, 90,  160, 82,  59,  214, 179, 41,  227, 47,  132,
+       83,  209, 0,   237, 32,  252, 177, 91,  106, 203, 190, 57,  74,  76,  88,  207,
+       208, 239, 170, 251, 67,  77,  51,  133, 69,  249, 2,   127, 80,  60,  159, 168,
+       81,  163, 64,  143, 146, 157, 56,  245, 188, 182, 218, 33,  16,  255, 243, 210,
+       205, 12,  19,  236, 95,  151, 68,  23,  196, 167, 126, 61,  100, 93,  25,  115,
+       96,  129, 79,  220, 34,  42,  144, 136, 70,  238, 184, 20,  222, 94,  11,  219,
+       224, 50,  58,  10,  73,  6,   36,  92,  194, 211, 172, 98,  145, 149, 228, 121,
+       231, 200, 55,  109, 141, 213, 78,  169, 108, 86,  244, 234, 101, 122, 174, 8,
+       186, 120, 37,  46,  28,  166, 180, 198, 232, 221, 116, 31,  75,  189, 139, 138,
+       112, 62,  181, 102, 72,  3,   246, 14,  97,  53,  87,  185, 134, 193, 29,  158,
+       225, 248, 152, 17,  105, 217, 142, 148, 155, 30,  135, 233, 206, 85,  40,  223,
+       140, 161, 137, 13,  191, 230, 66,  104, 65,  153, 45,  15,  176, 84,  187, 22
+};
+
+/* AES has a 32 bit word round constants for each round in the
+ * key schedule.  round_constant[i] is really Rcon[i+1] in FIPS187.
+ */
+static u32 round_constant[11] = {
+       0x01000000, 0x02000000, 0x04000000, 0x08000000,
+       0x10000000, 0x20000000, 0x40000000, 0x80000000,
+       0x1B000000, 0x36000000, 0x6C000000
+};
+
+/* Apply the s-box to each of the four occtets in w. */
+static u32 aes_ks_subword(const u32 w)
+{
+       u8 bytes[4];
+
+       *(u32*)(&bytes[0]) = w;
+       bytes[0] = aes_sbox[bytes[0]];
+       bytes[1] = aes_sbox[bytes[1]];
+       bytes[2] = aes_sbox[bytes[2]];
+       bytes[3] = aes_sbox[bytes[3]];
+       return *(u32*)(&bytes[0]);
+}
+
+/* The encrypt (forward) Rijndael key schedule algorithm pseudo code:
+ * (Note that AES words are 32 bit long)
+ *
+ * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk){
+ * word temp
+ * i = 0
+ * while (i < Nk) {
+ *   w[i] = word(key[4*i, 4*i + 1, 4*i + 2, 4*i + 3])
+ *   i = i + 1
+ * }
+ * i = Nk
+ *
+ * while (i < (Nb * (Nr + 1))) {
+ *   temp = w[i - 1]
+ *   if ((i mod Nk) == 0) {
+ *     temp = SubWord(RotWord(temp)) xor Rcon[i/Nk]
+ *   }
+ *   else if ((Nk > 6) && ((i mod Nk) == 4)) {
+ *     temp = SubWord(temp)
+ *   }
+ *   w[i] = w[i - Nk] xor temp
+ * }
+ * RotWord(t) does a 8 bit cyclic shift left on a 32 bit word.
+ * SubWord(t) applies the AES s-box individually to each octet
+ * in a 32 bit word.
+ *
+ * For AES Nk can have the values 4, 6, and 8 (corresponding to
+ * values for Nr of 10, 12, and 14).  Nb is always 4.
+ *
+ * To construct w[i], w[i - 1] and w[i - Nk] must be
+ * available.  Consequently we must keep a state of the last Nk words
+ * to be able to create the last round keys.
+ */
+static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned  char *key, unsigned int keylength)
+{
+       u32 temp;
+       u32 w_ring[8]; /* nk is max 8, use elements 0..(nk - 1) as a ringbuffer */
+       u8  w_last_ix;
+       int i;
+       u8  nr, nk;
+
+       switch (keylength){
+       case 128:
+               nk = 4;
+               nr = 10;
+               break;
+       case 192:
+               nk = 6;
+               nr = 12;
+               break;
+       case 256:
+               nk = 8;
+               nr = 14;
+               break;
+       default:
+               panic("stream co-processor: bad aes key length in get_aes_decrypt_key\n");
+       };
+
+       /* Need to do host byte order correction here since key is byte oriented and the
+        * kx algorithm is word (u32) oriented. */
+       for (i = 0; i < nk; i+=1) {
+               w_ring[i] = be32_to_cpu(*(u32*)&key[4*i]);
+       }
+
+       i = (int)nk;
+       w_last_ix = i - 1;
+       while (i < (4 * (nr + 2))) {
+               temp = w_ring[w_last_ix];
+               if (!(i % nk)) {
+                       /* RotWord(temp) */
+                       temp = (temp << 8) | (temp >> 24);
+                       temp = aes_ks_subword(temp);
+                       temp ^= round_constant[i/nk - 1];
+               } else if ((nk > 6) && ((i % nk) == 4)) {
+                       temp = aes_ks_subword(temp);
+               }
+               w_last_ix = (w_last_ix + 1) % nk; /* This is the same as (i-Nk) mod Nk */
+               temp ^= w_ring[w_last_ix];
+               w_ring[w_last_ix] = temp;
+
+               /* We need the round keys for round Nr+1 and Nr+2 (round key
+                * Nr+2 is the round key beyond the last one used when
+                * encrypting).  Rounds are numbered starting from 0, Nr=10
+                * implies 11 rounds are used in encryption/decryption.
+                */
+               if (i >= (4 * nr)) {
+                       /* Need to do host byte order correction here, the key
+                        * is byte oriented. */
+                       *(u32*)dec_key = cpu_to_be32(temp);
+                       dec_key += 4;
+               }
+               ++i;
+       }
+}
+
+
+/**** Job/operation management. ****/
+
+int cryptocop_job_queue_insert_csum(struct cryptocop_operation *operation)
+{
+       return cryptocop_job_queue_insert(cryptocop_prio_kernel_csum, operation);
+}
+
+int cryptocop_job_queue_insert_crypto(struct cryptocop_operation *operation)
+{
+       return cryptocop_job_queue_insert(cryptocop_prio_kernel, operation);
+}
+
+int cryptocop_job_queue_insert_user_job(struct cryptocop_operation *operation)
+{
+       return cryptocop_job_queue_insert(cryptocop_prio_user, operation);
+}
+
+static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation)
+{
+       int                           ret;
+       struct cryptocop_prio_job     *pj = NULL;
+       unsigned long int             flags;
+
+       DEBUG(printk("cryptocop_job_queue_insert(%d, 0x%p)\n", prio, operation));
+
+       if (!operation || !operation->cb){
+               DEBUG_API(printk("cryptocop_job_queue_insert oper=0x%p, NULL operation or callback\n", operation));
+               return -EINVAL;
+       }
+
+       if ((ret = cryptocop_job_setup(&pj, operation)) != 0){
+               DEBUG_API(printk("cryptocop_job_queue_insert: job setup failed\n"));
+               return ret;
+       }
+       assert(pj != NULL);
+
+       spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+       list_add_tail(&pj->node, &cryptocop_job_queues[prio].jobs);
+       spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+
+       /* Make sure a job is running */
+       cryptocop_start_job();
+       return 0;
+}
+
+static void cryptocop_do_tasklet(unsigned long unused);
+DECLARE_TASKLET (cryptocop_tasklet, cryptocop_do_tasklet, 0);
+
+static void cryptocop_do_tasklet(unsigned long unused)
+{
+       struct list_head             *node;
+       struct cryptocop_prio_job    *pj = NULL;
+       unsigned long                flags;
+
+       DEBUG(printk("cryptocop_do_tasklet: entering\n"));
+
+       do {
+               spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);
+               if (!list_empty(&cryptocop_completed_jobs)){
+                       node = cryptocop_completed_jobs.next;
+                       list_del(node);
+                       pj = list_entry(node, struct cryptocop_prio_job, node);
+               } else {
+                       pj = NULL;
+               }
+               spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);
+               if (pj) {
+                       assert(pj->oper != NULL);
+
+                       /* Notify consumer of operation completeness. */
+                       DEBUG(printk("cryptocop_do_tasklet: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+
+                       pj->oper->operation_status = 0; /* Job is completed. */
+                       pj->oper->cb(pj->oper, pj->oper->cb_data);
+                       delete_internal_operation(pj->iop);
+                       kfree(pj);
+               }
+       } while (pj != NULL);
+
+       DEBUG(printk("cryptocop_do_tasklet: exiting\n"));
+}
+
+static irqreturn_t
+dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct cryptocop_prio_job *done_job;
+       reg_dma_rw_ack_intr ack_intr = {
+               .data = 1,
+       };
+
+       REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr);
+
+       DEBUG(printk("cryptocop DMA done\n"));
+
+       spin_lock(&running_job_lock);
+       if (cryptocop_running_job == NULL){
+               printk("stream co-processor got interrupt when not busy\n");
+               spin_unlock(&running_job_lock);
+               return IRQ_HANDLED;
+       }
+       done_job = cryptocop_running_job;
+       cryptocop_running_job = NULL;
+       spin_unlock(&running_job_lock);
+
+       /* Start processing a job. */
+       if (!spin_trylock(&cryptocop_process_lock)){
+               DEBUG(printk("cryptocop irq handler, not starting a job\n"));
+       } else {
+               cryptocop_start_job();
+               spin_unlock(&cryptocop_process_lock);
+       }
+
+       done_job->oper->operation_status = 0; /* Job is completed. */
+       if (done_job->oper->fast_callback){
+               /* This operation wants callback from interrupt. */
+               done_job->oper->cb(done_job->oper, done_job->oper->cb_data);
+               delete_internal_operation(done_job->iop);
+               kfree(done_job);
+       } else {
+               spin_lock(&cryptocop_completed_jobs_lock);
+               list_add_tail(&(done_job->node), &cryptocop_completed_jobs);
+               spin_unlock(&cryptocop_completed_jobs_lock);
+               tasklet_schedule(&cryptocop_tasklet);
+       }
+
+       DEBUG(printk("cryptocop leave irq handler\n"));
+       return IRQ_HANDLED;
+}
+
+
+/* Setup interrupts and DMA channels. */
+static int init_cryptocop(void)
+{
+       unsigned long          flags;
+       reg_intr_vect_rw_mask  intr_mask;
+       reg_dma_rw_cfg         dma_cfg = {.en = 1};
+       reg_dma_rw_intr_mask   intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */
+       reg_dma_rw_ack_intr    ack_intr = {.data = 1,.in_eop = 1 };
+       reg_strcop_rw_cfg      strcop_cfg = {
+               .ipend = regk_strcop_little,
+               .td1 = regk_strcop_e,
+               .td2 = regk_strcop_d,
+               .td3 = regk_strcop_e,
+               .ignore_sync = 0,
+               .en = 1
+       };
+
+       if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9");
+
+       (void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
+       (void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
+
+       local_irq_save(flags);
+
+       /* Reset and enable the cryptocop. */
+       strcop_cfg.en = 0;
+       REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);
+       strcop_cfg.en = 1;
+       REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);
+
+       /* Enable DMA9 interrupt */
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.dma9 = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+       /* Enable DMAs. */
+       REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
+       REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+
+       /* Set up wordsize = 4 for DMAs. */
+       DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4);
+       DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4);
+
+       /* Enable interrupts. */
+       REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+
+       /* Clear intr ack. */
+       REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+/* Free used cryptocop hw resources (interrupt and DMA channels). */
+static void release_cryptocop(void)
+{
+       unsigned long          flags;
+       reg_intr_vect_rw_mask  intr_mask;
+       reg_dma_rw_cfg         dma_cfg = {.en = 0};
+       reg_dma_rw_intr_mask   intr_mask_in = {0};
+       reg_dma_rw_ack_intr    ack_intr = {.data = 1,.in_eop = 1 };
+
+       local_irq_save(flags);
+
+       /* Clear intr ack. */
+       REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
+
+       /* Disable DMA9 interrupt */
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.dma9 = 0;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+       /* Disable DMAs. */
+       REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
+       REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+
+       /* Disable interrupts. */
+       REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+
+       local_irq_restore(flags);
+
+       free_irq(DMA9_INTR_VECT, NULL);
+
+       (void)crisv32_free_dma(8);
+       (void)crisv32_free_dma(9);
+}
+
+
+/* Init job queue. */
+static int cryptocop_job_queue_init(void)
+{
+       int i;
+
+       INIT_LIST_HEAD(&cryptocop_completed_jobs);
+
+       for (i = 0; i < cryptocop_prio_no_prios; i++){
+               cryptocop_job_queues[i].prio = (cryptocop_queue_priority)i;
+               INIT_LIST_HEAD(&cryptocop_job_queues[i].jobs);
+       }
+       return 0;
+}
+
+
+static void cryptocop_job_queue_close(void)
+{
+       struct list_head               *node, *tmp;
+       struct cryptocop_prio_job      *pj = NULL;
+       unsigned long int              process_flags, flags;
+       int                            i;
+
+       /* FIXME: This is as yet untested code. */
+
+       /* Stop strcop from getting an operation to process while we are closing the
+          module. */
+       spin_lock_irqsave(&cryptocop_process_lock, process_flags);
+
+       /* Empty the job queue. */
+       spin_lock_irqsave(&cryptocop_process_lock, process_flags);
+       for (i = 0; i < cryptocop_prio_no_prios; i++){
+               if (!list_empty(&(cryptocop_job_queues[i].jobs))){
+                       list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {
+                               pj = list_entry(node, struct cryptocop_prio_job, node);
+                               list_del(node);
+
+                               /* Call callback to notify consumer of job removal. */
+                               DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+                               pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+                               pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+                               delete_internal_operation(pj->iop);
+                               kfree(pj);
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&cryptocop_process_lock, process_flags);
+
+       /* Remove the running job, if any. */
+       spin_lock_irqsave(&running_job_lock, flags);
+       if (cryptocop_running_job){
+               reg_strcop_rw_cfg rw_cfg;
+               reg_dma_rw_cfg    dma_out_cfg, dma_in_cfg;
+
+               /* Stop DMA. */
+               dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg);
+               dma_out_cfg.en = regk_dma_no;
+               REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg);
+
+               dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg);
+               dma_in_cfg.en = regk_dma_no;
+               REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg);
+
+               /* Disble the cryptocop. */
+               rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg);
+               rw_cfg.en = 0;
+               REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);
+
+               pj = cryptocop_running_job;
+               cryptocop_running_job = NULL;
+
+               /* Call callback to notify consumer of job removal. */
+               DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+               pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+               pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+               delete_internal_operation(pj->iop);
+               kfree(pj);
+       }
+       spin_unlock_irqrestore(&running_job_lock, flags);
+
+       /* Remove completed jobs, if any. */
+       spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);
+
+       list_for_each_safe(node, tmp, &cryptocop_completed_jobs) {
+               pj = list_entry(node, struct cryptocop_prio_job, node);
+               list_del(node);
+               /* Call callback to notify consumer of job removal. */
+               DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+               pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+               pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+               delete_internal_operation(pj->iop);
+               kfree(pj);
+       }
+       spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);
+}
+
+
+static void cryptocop_start_job(void)
+{
+       int                          i;
+       struct cryptocop_prio_job    *pj;
+       unsigned long int            flags;
+       unsigned long int            running_job_flags;
+       reg_strcop_rw_cfg            rw_cfg = {.en = 1, .ignore_sync = 0};
+
+       DEBUG(printk("cryptocop_start_job: entering\n"));
+
+       spin_lock_irqsave(&running_job_lock, running_job_flags);
+       if (cryptocop_running_job != NULL){
+               /* Already running. */
+               DEBUG(printk("cryptocop_start_job: already running, exit\n"));
+               spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+               return;
+       }
+       spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+
+       /* Check the queues in priority order. */
+       for (i = cryptocop_prio_kernel_csum; (i < cryptocop_prio_no_prios) && list_empty(&cryptocop_job_queues[i].jobs); i++);
+       if (i == cryptocop_prio_no_prios) {
+               spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+               spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+               DEBUG(printk("cryptocop_start_job: no jobs to run\n"));
+               return; /* No jobs to run */
+       }
+       DEBUG(printk("starting job for prio %d\n", i));
+
+       /* TODO: Do not starve lower priority jobs.  Let in a lower
+        * prio job for every N-th processed higher prio job or some
+        * other scheduling policy.  This could reasonably be
+        * tweakable since the optimal balance would depend on the
+        * type of load on the system. */
+
+       /* Pull the DMA lists from the job and start the DMA client. */
+       pj = list_entry(cryptocop_job_queues[i].jobs.next, struct cryptocop_prio_job, node);
+       list_del(&pj->node);
+       spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+       cryptocop_running_job = pj;
+
+       /* Set config register (3DES and CSUM modes). */
+       switch (pj->iop->tdes_mode){
+       case cryptocop_3des_eee:
+               rw_cfg.td1 = regk_strcop_e;
+               rw_cfg.td2 = regk_strcop_e;
+               rw_cfg.td3 = regk_strcop_e;
+               break;
+       case cryptocop_3des_eed:
+               rw_cfg.td1 = regk_strcop_e;
+               rw_cfg.td2 = regk_strcop_e;
+               rw_cfg.td3 = regk_strcop_d;
+               break;
+       case cryptocop_3des_ede:
+               rw_cfg.td1 = regk_strcop_e;
+               rw_cfg.td2 = regk_strcop_d;
+               rw_cfg.td3 = regk_strcop_e;
+               break;
+       case cryptocop_3des_edd:
+               rw_cfg.td1 = regk_strcop_e;
+               rw_cfg.td2 = regk_strcop_d;
+               rw_cfg.td3 = regk_strcop_d;
+               break;
+       case cryptocop_3des_dee:
+               rw_cfg.td1 = regk_strcop_d;
+               rw_cfg.td2 = regk_strcop_e;
+               rw_cfg.td3 = regk_strcop_e;
+               break;
+       case cryptocop_3des_ded:
+               rw_cfg.td1 = regk_strcop_d;
+               rw_cfg.td2 = regk_strcop_e;
+               rw_cfg.td3 = regk_strcop_d;
+               break;
+       case cryptocop_3des_dde:
+               rw_cfg.td1 = regk_strcop_d;
+               rw_cfg.td2 = regk_strcop_d;
+               rw_cfg.td3 = regk_strcop_e;
+               break;
+       case cryptocop_3des_ddd:
+               rw_cfg.td1 = regk_strcop_d;
+               rw_cfg.td2 = regk_strcop_d;
+               rw_cfg.td3 = regk_strcop_d;
+               break;
+       default:
+               DEBUG(printk("cryptocop_setup_dma_list: bad 3DES mode\n"));
+       }
+       switch (pj->iop->csum_mode){
+       case cryptocop_csum_le:
+               rw_cfg.ipend = regk_strcop_little;
+               break;
+       case cryptocop_csum_be:
+               rw_cfg.ipend = regk_strcop_big;
+               break;
+       default:
+               DEBUG(printk("cryptocop_setup_dma_list: bad checksum mode\n"));
+       }
+       REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);
+
+       DEBUG(printk("cryptocop_start_job: starting DMA, new cryptocop_running_job=0x%p\n"
+                    "ctx_in: 0x%p, phys: 0x%p\n"
+                    "ctx_out: 0x%p, phys: 0x%p\n",
+                    pj,
+                    &pj->iop->ctx_in, (char*)virt_to_phys(&pj->iop->ctx_in),
+                    &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out)));
+
+       /* Start input DMA. */
+       DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in));
+
+       /* Start output DMA. */
+       DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out));
+
+       spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+       DEBUG(printk("cryptocop_start_job: exiting\n"));
+}
+
+
+static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation)
+{
+       int  err;
+       int  alloc_flag = operation->in_interrupt ? GFP_ATOMIC : GFP_KERNEL;
+       void *iop_alloc_ptr = NULL;
+
+       *pj = kmalloc(sizeof (struct cryptocop_prio_job), alloc_flag);
+       if (!*pj) return -ENOMEM;
+
+       DEBUG(printk("cryptocop_job_setup: operation=0x%p\n", operation));
+
+       (*pj)->oper = operation;
+       DEBUG(printk("cryptocop_job_setup, cb=0x%p cb_data=0x%p\n",  (*pj)->oper->cb, (*pj)->oper->cb_data));
+
+       if (operation->use_dmalists) {
+               DEBUG(print_user_dma_lists(&operation->list_op));
+               if (!operation->list_op.inlist || !operation->list_op.outlist || !operation->list_op.out_data_buf || !operation->list_op.in_data_buf){
+                       DEBUG_API(printk("cryptocop_job_setup: bad indata (use_dmalists)\n"));
+                       kfree(*pj);
+                       return -EINVAL;
+               }
+               iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);
+               if (!iop_alloc_ptr) {
+                       DEBUG_API(printk("cryptocop_job_setup: kmalloc cryptocop_int_operation\n"));
+                       kfree(*pj);
+                       return -ENOMEM;
+               }
+               (*pj)->iop = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));
+               DEBUG(memset((*pj)->iop, 0xff, sizeof(struct cryptocop_int_operation)));
+               (*pj)->iop->alloc_ptr = iop_alloc_ptr;
+               (*pj)->iop->sid = operation->sid;
+               (*pj)->iop->cdesc_out = NULL;
+               (*pj)->iop->cdesc_in = NULL;
+               (*pj)->iop->tdes_mode = operation->list_op.tdes_mode;
+               (*pj)->iop->csum_mode = operation->list_op.csum_mode;
+               (*pj)->iop->ddesc_out = operation->list_op.outlist;
+               (*pj)->iop->ddesc_in = operation->list_op.inlist;
+
+               /* Setup DMA contexts. */
+               (*pj)->iop->ctx_out.next = NULL;
+               (*pj)->iop->ctx_out.eol = 1;
+               (*pj)->iop->ctx_out.saved_data = operation->list_op.outlist;
+               (*pj)->iop->ctx_out.saved_data_buf = operation->list_op.out_data_buf;
+
+               (*pj)->iop->ctx_in.next = NULL;
+               (*pj)->iop->ctx_in.eol = 1;
+               (*pj)->iop->ctx_in.saved_data = operation->list_op.inlist;
+               (*pj)->iop->ctx_in.saved_data_buf = operation->list_op.in_data_buf;
+       } else {
+               if ((err = cryptocop_setup_dma_list(operation, &(*pj)->iop, alloc_flag))) {
+                       DEBUG_API(printk("cryptocop_job_setup: cryptocop_setup_dma_list failed %d\n", err));
+                       kfree(*pj);
+                       return err;
+               }
+       }
+       DEBUG(print_dma_descriptors((*pj)->iop));
+
+       DEBUG(printk("cryptocop_job_setup, DMA list setup successful\n"));
+
+       return 0;
+}
+
+
+static int cryptocop_open(struct inode *inode, struct file *filp)
+{
+       int p = MINOR(inode->i_rdev);
+
+       if (p != CRYPTOCOP_MINOR) return -EINVAL;
+
+       filp->private_data = NULL;
+       return 0;
+}
+
+
+static int cryptocop_release(struct inode *inode, struct file *filp)
+{
+       struct cryptocop_private *dev = filp->private_data;
+       struct cryptocop_private *dev_next;
+
+       while (dev){
+               dev_next = dev->next;
+               if (dev->sid != CRYPTOCOP_SESSION_ID_NONE) {
+                       (void)cryptocop_free_session(dev->sid);
+               }
+               kfree(dev);
+               dev = dev_next;
+       }
+
+       return 0;
+}
+
+
+static int cryptocop_ioctl_close_session(struct inode *inode, struct file *filp,
+                                        unsigned int cmd, unsigned long arg)
+{
+       struct cryptocop_private  *dev = filp->private_data;
+       struct cryptocop_private  *prev_dev = NULL;
+       struct strcop_session_op  *sess_op = (struct strcop_session_op *)arg;
+       struct strcop_session_op  sop;
+       int                       err;
+
+       DEBUG(printk("cryptocop_ioctl_close_session\n"));
+
+       if (!access_ok(VERIFY_READ, sess_op, sizeof(struct strcop_session_op)))
+               return -EFAULT;
+       err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));
+       if (err) return -EFAULT;
+
+       while (dev && (dev->sid != sop.ses_id)) {
+               prev_dev = dev;
+               dev = dev->next;
+       }
+       if (dev){
+               if (prev_dev){
+                       prev_dev->next = dev->next;
+               } else {
+                       filp->private_data = dev->next;
+               }
+               err = cryptocop_free_session(dev->sid);
+               if (err) return -EFAULT;
+       } else {
+               DEBUG_API(printk("cryptocop_ioctl_close_session: session %lld not found\n", sop.ses_id));
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+static void ioctl_process_job_callback(struct cryptocop_operation *op, void*cb_data)
+{
+       struct ioctl_job_cb_ctx *jc = (struct ioctl_job_cb_ctx *)cb_data;
+
+       DEBUG(printk("ioctl_process_job_callback: op=0x%p, cb_data=0x%p\n", op, cb_data));
+
+       jc->processed = 1;
+       wake_up(&cryptocop_ioc_process_wq);
+}
+
+
+#define CRYPTOCOP_IOCTL_CIPHER_TID  (1)
+#define CRYPTOCOP_IOCTL_DIGEST_TID  (2)
+#define CRYPTOCOP_IOCTL_CSUM_TID    (3)
+
+static size_t first_cfg_change_ix(struct strcop_crypto_op *crp_op)
+{
+       size_t ch_ix = 0;
+
+       if (crp_op->do_cipher) ch_ix = crp_op->cipher_start;
+       if (crp_op->do_digest && (crp_op->digest_start < ch_ix)) ch_ix = crp_op->digest_start;
+       if (crp_op->do_csum && (crp_op->csum_start < ch_ix)) ch_ix = crp_op->csum_start;
+
+       DEBUG(printk("first_cfg_change_ix: ix=%d\n", ch_ix));
+       return ch_ix;
+}
+
+
+static size_t next_cfg_change_ix(struct strcop_crypto_op *crp_op, size_t ix)
+{
+       size_t ch_ix = INT_MAX;
+       size_t tmp_ix = 0;
+
+       if (crp_op->do_cipher && ((crp_op->cipher_start + crp_op->cipher_len) > ix)){
+               if (crp_op->cipher_start > ix) {
+                       ch_ix = crp_op->cipher_start;
+               } else {
+                       ch_ix = crp_op->cipher_start + crp_op->cipher_len;
+               }
+       }
+       if (crp_op->do_digest && ((crp_op->digest_start + crp_op->digest_len) > ix)){
+               if (crp_op->digest_start > ix) {
+                       tmp_ix = crp_op->digest_start;
+               } else {
+                       tmp_ix = crp_op->digest_start + crp_op->digest_len;
+               }
+               if (tmp_ix < ch_ix) ch_ix = tmp_ix;
+       }
+       if (crp_op->do_csum && ((crp_op->csum_start + crp_op->csum_len) > ix)){
+               if (crp_op->csum_start > ix) {
+                       tmp_ix = crp_op->csum_start;
+               } else {
+                       tmp_ix = crp_op->csum_start + crp_op->csum_len;
+               }
+               if (tmp_ix < ch_ix) ch_ix = tmp_ix;
+       }
+       if (ch_ix == INT_MAX) ch_ix = ix;
+       DEBUG(printk("next_cfg_change_ix prev ix=%d, next ix=%d\n", ix, ch_ix));
+       return ch_ix;
+}
+
+
+/* Map map_length bytes from the pages starting on *pageix and *pageoffset to iovecs starting on *iovix.
+ * Return -1 for ok, 0 for fail. */
+static int map_pages_to_iovec(struct iovec *iov, int iovlen, int *iovix, struct page **pages, int nopages, int *pageix, int *pageoffset, int map_length )
+{
+       int tmplen;
+
+       assert(iov != NULL);
+       assert(iovix != NULL);
+       assert(pages != NULL);
+       assert(pageix != NULL);
+       assert(pageoffset != NULL);
+
+       DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));
+
+       while (map_length > 0){
+               DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));
+               if (*iovix >= iovlen){
+                       DEBUG_API(printk("map_page_to_iovec: *iovix=%d >= iovlen=%d\n", *iovix, iovlen));
+                       return 0;
+               }
+               if (*pageix >= nopages){
+                       DEBUG_API(printk("map_page_to_iovec: *pageix=%d >= nopages=%d\n", *pageix, nopages));
+                       return 0;
+               }
+               iov[*iovix].iov_base = (unsigned char*)page_address(pages[*pageix]) + *pageoffset;
+               tmplen = PAGE_SIZE - *pageoffset;
+               if (tmplen < map_length){
+                       (*pageoffset) = 0;
+                       (*pageix)++;
+               } else {
+                       tmplen = map_length;
+                       (*pageoffset) += map_length;
+               }
+               DEBUG(printk("mapping %d bytes from page %d (or %d) to iovec %d\n", tmplen, *pageix, *pageix-1, *iovix));
+               iov[*iovix].iov_len = tmplen;
+               map_length -= tmplen;
+               (*iovix)++;
+       }
+       DEBUG(printk("map_page_to_iovec, exit, *iovix=%d\n", *iovix));
+       return -1;
+}
+
+
+
+static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       int                             i;
+       struct cryptocop_private        *dev = filp->private_data;
+       struct strcop_crypto_op         *crp_oper = (struct strcop_crypto_op *)arg;
+       struct strcop_crypto_op         oper = {0};
+       int                             err = 0;
+       struct cryptocop_operation      *cop = NULL;
+
+       struct ioctl_job_cb_ctx         *jc = NULL;
+
+       struct page                     **inpages = NULL;
+       struct page                     **outpages = NULL;
+       int                             noinpages = 0;
+       int                             nooutpages = 0;
+
+       struct cryptocop_desc           descs[5]; /* Max 5 descriptors are needed, there are three transforms that
+                                                  * can get connected/disconnected on different places in the indata. */
+       struct cryptocop_desc_cfg       dcfgs[5*3];
+       int                             desc_ix = 0;
+       int                             dcfg_ix = 0;
+       struct cryptocop_tfrm_cfg       ciph_tcfg = {0};
+       struct cryptocop_tfrm_cfg       digest_tcfg = {0};
+       struct cryptocop_tfrm_cfg       csum_tcfg = {0};
+
+       unsigned char                   *digest_result = NULL;
+       int                             digest_length = 0;
+       int                             cblocklen = 0;
+       unsigned char                   csum_result[CSUM_BLOCK_LENGTH];
+       struct cryptocop_session        *sess;
+
+       int    iovlen = 0;
+       int    iovix = 0;
+       int    pageix = 0;
+       int    pageoffset = 0;
+
+       size_t prev_ix = 0;
+       size_t next_ix;
+
+       int    cipher_active, digest_active, csum_active;
+       int    end_digest, end_csum;
+       int    digest_done = 0;
+       int    cipher_done = 0;
+       int    csum_done = 0;
+
+       DEBUG(printk("cryptocop_ioctl_process\n"));
+
+       if (!access_ok(VERIFY_WRITE, crp_oper, sizeof(struct strcop_crypto_op))){
+               DEBUG_API(printk("cryptocop_ioctl_process: !access_ok crp_oper!\n"));
+               return -EFAULT;
+       }
+       if (copy_from_user(&oper, crp_oper, sizeof(struct strcop_crypto_op))) {
+               DEBUG_API(printk("cryptocop_ioctl_process: copy_from_user\n"));
+               return -EFAULT;
+       }
+       DEBUG(print_strcop_crypto_op(&oper));
+
+       while (dev && dev->sid != oper.ses_id) dev = dev->next;
+       if (!dev){
+               DEBUG_API(printk("cryptocop_ioctl_process: session %lld not found\n", oper.ses_id));
+               return -EINVAL;
+       }
+
+       /* Check buffers. */
+       if (((oper.indata + oper.inlen) < oper.indata) || ((oper.cipher_outdata + oper.cipher_outlen) < oper.cipher_outdata)){
+               DEBUG_API(printk("cryptocop_ioctl_process: user buffers wrapped around, bad user!\n"));
+               return -EINVAL;
+       }
+
+       if (!access_ok(VERIFY_WRITE, oper.cipher_outdata, oper.cipher_outlen)){
+               DEBUG_API(printk("cryptocop_ioctl_process: !access_ok out data!\n"));
+               return -EFAULT;
+       }
+       if (!access_ok(VERIFY_READ, oper.indata, oper.inlen)){
+               DEBUG_API(printk("cryptocop_ioctl_process: !access_ok in data!\n"));
+               return -EFAULT;
+       }
+
+       cop = kmalloc(sizeof(struct cryptocop_operation), GFP_KERNEL);
+       if (!cop) {
+               DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));
+               return -ENOMEM;
+       }
+       jc = kmalloc(sizeof(struct ioctl_job_cb_ctx), GFP_KERNEL);
+       if (!jc) {
+               DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));
+               err = -ENOMEM;
+               goto error_cleanup;
+       }
+       jc->processed = 0;
+
+       cop->cb_data = jc;
+       cop->cb = ioctl_process_job_callback;
+       cop->operation_status = 0;
+       cop->use_dmalists = 0;
+       cop->in_interrupt = 0;
+       cop->fast_callback = 0;
+       cop->tfrm_op.tfrm_cfg = NULL;
+       cop->tfrm_op.desc = NULL;
+       cop->tfrm_op.indata = NULL;
+       cop->tfrm_op.incount = 0;
+       cop->tfrm_op.inlen = 0;
+       cop->tfrm_op.outdata = NULL;
+       cop->tfrm_op.outcount = 0;
+       cop->tfrm_op.outlen = 0;
+
+       sess = get_session(oper.ses_id);
+       if (!sess){
+               DEBUG_API(printk("cryptocop_ioctl_process: bad session id.\n"));
+               kfree(cop);
+               kfree(jc);
+               return -EINVAL;
+       }
+
+       if (oper.do_cipher) {
+               unsigned int                    cipher_outlen = 0;
+               struct cryptocop_transform_ctx  *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_CIPHER_TID);
+               if (!tc) {
+                       DEBUG_API(printk("cryptocop_ioctl_process: no cipher transform in session.\n"));
+                       err = -EINVAL;
+                       goto error_cleanup;
+               }
+               ciph_tcfg.tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+               ciph_tcfg.inject_ix = 0;
+               ciph_tcfg.flags = 0;
+               if ((oper.cipher_start < 0) || (oper.cipher_len <= 0) || (oper.cipher_start > oper.inlen) || ((oper.cipher_start + oper.cipher_len) > oper.inlen)){
+                       DEBUG_API(printk("cryptocop_ioctl_process: bad cipher length\n"));
+                       kfree(cop);
+                       kfree(jc);
+                       return -EINVAL;
+               }
+               cblocklen = tc->init.alg == cryptocop_alg_aes ? AES_BLOCK_LENGTH : DES_BLOCK_LENGTH;
+               if (oper.cipher_len % cblocklen) {
+                       kfree(cop);
+                       kfree(jc);
+                       DEBUG_API(printk("cryptocop_ioctl_process: cipher inlength not multiple of block length.\n"));
+                       return -EINVAL;
+               }
+               cipher_outlen = oper.cipher_len;
+               if (tc->init.cipher_mode == cryptocop_cipher_mode_cbc){
+                       if (oper.cipher_explicit) {
+                               ciph_tcfg.flags |= CRYPTOCOP_EXPLICIT_IV;
+                               memcpy(ciph_tcfg.iv, oper.cipher_iv, cblocklen);
+                       } else {
+                               cipher_outlen = oper.cipher_len - cblocklen;
+                       }
+               } else {
+                       if (oper.cipher_explicit){
+                               kfree(cop);
+                               kfree(jc);
+                               DEBUG_API(printk("cryptocop_ioctl_process: explicit_iv when not CBC mode\n"));
+                               return -EINVAL;
+                       }
+               }
+               if (oper.cipher_outlen != cipher_outlen) {
+                       kfree(cop);
+                       kfree(jc);
+                       DEBUG_API(printk("cryptocop_ioctl_process: cipher_outlen incorrect, should be %d not %d.\n", cipher_outlen, oper.cipher_outlen));
+                       return -EINVAL;
+               }
+
+               if (oper.decrypt){
+                       ciph_tcfg.flags |= CRYPTOCOP_DECRYPT;
+               } else {
+                       ciph_tcfg.flags |= CRYPTOCOP_ENCRYPT;
+               }
+               ciph_tcfg.next = cop->tfrm_op.tfrm_cfg;
+               cop->tfrm_op.tfrm_cfg = &ciph_tcfg;
+       }
+       if (oper.do_digest){
+               struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_DIGEST_TID);
+               if (!tc) {
+                       DEBUG_API(printk("cryptocop_ioctl_process: no digest transform in session.\n"));
+                       err = -EINVAL;
+                       goto error_cleanup;
+               }
+               digest_length = tc->init.alg == cryptocop_alg_md5 ? 16 : 20;
+               digest_result = kmalloc(digest_length, GFP_KERNEL);
+               if (!digest_result) {
+                       DEBUG_API(printk("cryptocop_ioctl_process: kmalloc digest_result\n"));
+                       err = -EINVAL;
+                       goto error_cleanup;
+               }
+               DEBUG(memset(digest_result, 0xff, digest_length));
+
+               digest_tcfg.tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+               digest_tcfg.inject_ix = 0;
+               ciph_tcfg.inject_ix += digest_length;
+               if ((oper.digest_start < 0) || (oper.digest_len <= 0) || (oper.digest_start > oper.inlen) || ((oper.digest_start + oper.digest_len) > oper.inlen)){
+                       DEBUG_API(printk("cryptocop_ioctl_process: bad digest length\n"));
+                       err = -EINVAL;
+                       goto error_cleanup;
+               }
+
+               digest_tcfg.next = cop->tfrm_op.tfrm_cfg;
+               cop->tfrm_op.tfrm_cfg = &digest_tcfg;
+       }
+       if (oper.do_csum){
+               csum_tcfg.tid = CRYPTOCOP_IOCTL_CSUM_TID;
+               csum_tcfg.inject_ix = digest_length;
+               ciph_tcfg.inject_ix += 2;
+
+               if ((oper.csum_start < 0) || (oper.csum_len <= 0) || (oper.csum_start > oper.inlen) || ((oper.csum_start + oper.csum_len) > oper.inlen)){
+                       DEBUG_API(printk("cryptocop_ioctl_process: bad csum length\n"));
+                       kfree(cop);
+                       kfree(jc);
+                       return -EINVAL;
+               }
+
+               csum_tcfg.next = cop->tfrm_op.tfrm_cfg;
+               cop->tfrm_op.tfrm_cfg = &csum_tcfg;
+       }
+
+       prev_ix = first_cfg_change_ix(&oper);
+       if (prev_ix > oper.inlen) {
+               DEBUG_API(printk("cryptocop_ioctl_process: length mismatch\n"));
+               nooutpages = noinpages = 0;
+               err = -EINVAL;
+               goto error_cleanup;
+       }
+       DEBUG(printk("cryptocop_ioctl_process: inlen=%d, cipher_outlen=%d\n", oper.inlen, oper.cipher_outlen));
+
+       /* Map user pages for in and out data of the operation. */
+       noinpages = (((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK) + oper.inlen - 1 - prev_ix + ~PAGE_MASK) >> PAGE_SHIFT;
+       DEBUG(printk("cryptocop_ioctl_process: noinpages=%d\n", noinpages));
+       inpages = kmalloc(noinpages * sizeof(struct page*), GFP_KERNEL);
+       if (!inpages){
+               DEBUG_API(printk("cryptocop_ioctl_process: kmalloc inpages\n"));
+               nooutpages = noinpages = 0;
+               err = -ENOMEM;
+               goto error_cleanup;
+       }
+       if (oper.do_cipher){
+               nooutpages = (((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) + oper.cipher_outlen - 1 + ~PAGE_MASK) >> PAGE_SHIFT;
+               DEBUG(printk("cryptocop_ioctl_process: nooutpages=%d\n", nooutpages));
+               outpages = kmalloc(nooutpages * sizeof(struct page*), GFP_KERNEL);
+               if (!outpages){
+                       DEBUG_API(printk("cryptocop_ioctl_process: kmalloc outpages\n"));
+                       nooutpages = noinpages = 0;
+                       err = -ENOMEM;
+                       goto error_cleanup;
+               }
+       }
+
+       /* Acquire the mm page semaphore. */
+       down_read(&current->mm->mmap_sem);
+
+       err = get_user_pages(current,
+                            current->mm,
+                            (unsigned long int)(oper.indata + prev_ix),
+                            noinpages,
+                            0,  /* read access only for in data */
+                            0, /* no force */
+                            inpages,
+                            NULL);
+
+       if (err < 0) {
+               up_read(&current->mm->mmap_sem);
+               nooutpages = noinpages = 0;
+               DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages indata\n"));
+               goto error_cleanup;
+       }
+       noinpages = err;
+       if (oper.do_cipher){
+               err = get_user_pages(current,
+                                    current->mm,
+                                    (unsigned long int)oper.cipher_outdata,
+                                    nooutpages,
+                                    1, /* write access for out data */
+                                    0, /* no force */
+                                    outpages,
+                                    NULL);
+               up_read(&current->mm->mmap_sem);
+               if (err < 0) {
+                       nooutpages = 0;
+                       DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages outdata\n"));
+                       goto error_cleanup;
+               }
+               nooutpages = err;
+       } else {
+               up_read(&current->mm->mmap_sem);
+       }
+
+       /* Add 6 to nooutpages to make room for possibly inserted buffers for storing digest and
+        * csum output and splits when units are (dis-)connected. */
+       cop->tfrm_op.indata = kmalloc((noinpages) * sizeof(struct iovec), GFP_KERNEL);
+       cop->tfrm_op.outdata = kmalloc((6 + nooutpages) * sizeof(struct iovec), GFP_KERNEL);
+       if (!cop->tfrm_op.indata || !cop->tfrm_op.outdata) {
+               DEBUG_API(printk("cryptocop_ioctl_process: kmalloc iovecs\n"));
+               err = -ENOMEM;
+               goto error_cleanup;
+       }
+
+       cop->tfrm_op.inlen = oper.inlen - prev_ix;
+       cop->tfrm_op.outlen = 0;
+       if (oper.do_cipher) cop->tfrm_op.outlen += oper.cipher_outlen;
+       if (oper.do_digest) cop->tfrm_op.outlen += digest_length;
+       if (oper.do_csum) cop->tfrm_op.outlen += 2;
+
+       /* Setup the in iovecs. */
+       cop->tfrm_op.incount = noinpages;
+       if (noinpages > 1){
+               size_t tmplen = cop->tfrm_op.inlen;
+
+               cop->tfrm_op.indata[0].iov_len = PAGE_SIZE - ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+               cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+               tmplen -= cop->tfrm_op.indata[0].iov_len;
+               for (i = 1; i<noinpages; i++){
+                       cop->tfrm_op.indata[i].iov_len = tmplen < PAGE_SIZE ? tmplen : PAGE_SIZE;
+                       cop->tfrm_op.indata[i].iov_base = (unsigned char*)page_address(inpages[i]);
+                       tmplen -= PAGE_SIZE;
+               }
+       } else {
+               cop->tfrm_op.indata[0].iov_len = oper.inlen - prev_ix;
+               cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+       }
+
+       iovlen = nooutpages + 6;
+       pageoffset = oper.do_cipher ? ((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) : 0;
+
+       next_ix = next_cfg_change_ix(&oper, prev_ix);
+       if (prev_ix == next_ix){
+               DEBUG_API(printk("cryptocop_ioctl_process: length configuration broken.\n"));
+               err = -EINVAL;  /* This should be impossible barring bugs. */
+               goto error_cleanup;
+       }
+       while (prev_ix != next_ix){
+               end_digest = end_csum = cipher_active = digest_active = csum_active = 0;
+               descs[desc_ix].cfg = NULL;
+               descs[desc_ix].length = next_ix - prev_ix;
+
+               if (oper.do_cipher && (oper.cipher_start < next_ix) && (prev_ix < (oper.cipher_start + oper.cipher_len))) {
+                       dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+                       dcfgs[dcfg_ix].src = cryptocop_source_dma;
+                       cipher_active = 1;
+
+                       if (next_ix == (oper.cipher_start + oper.cipher_len)){
+                               cipher_done = 1;
+                               dcfgs[dcfg_ix].last = 1;
+                       } else {
+                               dcfgs[dcfg_ix].last = 0;
+                       }
+                       dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+                       descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+                       ++dcfg_ix;
+               }
+               if (oper.do_digest && (oper.digest_start < next_ix) && (prev_ix < (oper.digest_start + oper.digest_len))) {
+                       digest_active = 1;
+                       dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+                       dcfgs[dcfg_ix].src = cryptocop_source_dma;
+                       if (next_ix == (oper.digest_start + oper.digest_len)){
+                               assert(!digest_done);
+                               digest_done = 1;
+                               dcfgs[dcfg_ix].last = 1;
+                       } else {
+                               dcfgs[dcfg_ix].last = 0;
+                       }
+                       dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+                       descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+                       ++dcfg_ix;
+               }
+               if (oper.do_csum && (oper.csum_start < next_ix) && (prev_ix < (oper.csum_start + oper.csum_len))){
+                       csum_active = 1;
+                       dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CSUM_TID;
+                       dcfgs[dcfg_ix].src = cryptocop_source_dma;
+                       if (next_ix == (oper.csum_start + oper.csum_len)){
+                               csum_done = 1;
+                               dcfgs[dcfg_ix].last = 1;
+                       } else {
+                               dcfgs[dcfg_ix].last = 0;
+                       }
+                       dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+                       descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+                       ++dcfg_ix;
+               }
+               if (!descs[desc_ix].cfg){
+                       DEBUG_API(printk("cryptocop_ioctl_process: data segment %d (%d to %d) had no active transforms\n", desc_ix, prev_ix, next_ix));
+                       err = -EINVAL;
+                       goto error_cleanup;
+               }
+               descs[desc_ix].next = &(descs[desc_ix]) + 1;
+               ++desc_ix;
+               prev_ix = next_ix;
+               next_ix = next_cfg_change_ix(&oper, prev_ix);
+       }
+       if (desc_ix > 0){
+               descs[desc_ix-1].next = NULL;
+       } else {
+               descs[0].next = NULL;
+       }
+       if (oper.do_digest) {
+               DEBUG(printk("cryptocop_ioctl_process: mapping %d byte digest output to iovec %d\n", digest_length, iovix));
+               /* Add outdata iovec, length == <length of type of digest> */
+               cop->tfrm_op.outdata[iovix].iov_base = digest_result;
+               cop->tfrm_op.outdata[iovix].iov_len = digest_length;
+               ++iovix;
+       }
+       if (oper.do_csum) {
+               /* Add outdata iovec, length == 2, the length of csum. */
+               DEBUG(printk("cryptocop_ioctl_process: mapping 2 byte csum output to iovec %d\n", iovix));
+               /* Add outdata iovec, length == <length of type of digest> */
+               cop->tfrm_op.outdata[iovix].iov_base = csum_result;
+               cop->tfrm_op.outdata[iovix].iov_len = 2;
+               ++iovix;
+       }
+       if (oper.do_cipher) {
+               if (!map_pages_to_iovec(cop->tfrm_op.outdata, iovlen, &iovix, outpages, nooutpages, &pageix, &pageoffset, oper.cipher_outlen)){
+                       DEBUG_API(printk("cryptocop_ioctl_process: failed to map pages to iovec.\n"));
+                       err = -ENOSYS; /* This should be impossible barring bugs. */
+                       goto error_cleanup;
+               }
+       }
+       DEBUG(printk("cryptocop_ioctl_process: setting cop->tfrm_op.outcount %d\n", iovix));
+       cop->tfrm_op.outcount = iovix;
+       assert(iovix <= (nooutpages + 6));
+
+       cop->sid = oper.ses_id;
+       cop->tfrm_op.desc = &descs[0];
+
+       DEBUG(printk("cryptocop_ioctl_process: inserting job, cb_data=0x%p\n", cop->cb_data));
+
+       if ((err = cryptocop_job_queue_insert_user_job(cop)) != 0) {
+               DEBUG_API(printk("cryptocop_ioctl_process: insert job %d\n", err));
+               err = -EINVAL;
+               goto error_cleanup;
+       }
+
+       DEBUG(printk("cryptocop_ioctl_process: begin wait for result\n"));
+
+       wait_event(cryptocop_ioc_process_wq, (jc->processed != 0));
+       DEBUG(printk("cryptocop_ioctl_process: end wait for result\n"));
+        if (!jc->processed){
+               printk(KERN_WARNING "cryptocop_ioctl_process: job not processed at completion\n");
+               err = -EIO;
+               goto error_cleanup;
+       }
+
+       /* Job process done.  Cipher output should already be correct in job so no post processing of outdata. */
+       DEBUG(printk("cryptocop_ioctl_process: operation_status = %d\n", cop->operation_status));
+       if (cop->operation_status == 0){
+               if (oper.do_digest){
+                       DEBUG(printk("cryptocop_ioctl_process: copy %d bytes digest to user\n", digest_length));
+                       err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, digest), digest_result, digest_length);
+                       if (0 != err){
+                               DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, digest length %d, err %d\n", digest_length, err));
+                               err = -EFAULT;
+                               goto error_cleanup;
+                       }
+               }
+               if (oper.do_csum){
+                       DEBUG(printk("cryptocop_ioctl_process: copy 2 bytes checksum to user\n"));
+                       err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, csum), csum_result, 2);
+                       if (0 != err){
+                               DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, csum, err %d\n", err));
+                               err = -EFAULT;
+                               goto error_cleanup;
+                       }
+               }
+               err = 0;
+       } else {
+               DEBUG(printk("cryptocop_ioctl_process: returning err = operation_status = %d\n", cop->operation_status));
+               err = cop->operation_status;
+       }
+
+ error_cleanup:
+       /* Release page caches. */
+       for (i = 0; i < noinpages; i++){
+               put_page(inpages[i]);
+       }
+       for (i = 0; i < nooutpages; i++){
+               int spdl_err;
+               /* Mark output pages dirty. */
+               spdl_err = set_page_dirty_lock(outpages[i]);
+               DEBUG(if (spdl_err)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err));
+       }
+       for (i = 0; i < nooutpages; i++){
+               put_page(outpages[i]);
+       }
+
+       if (digest_result) kfree(digest_result);
+       if (inpages) kfree(inpages);
+       if (outpages) kfree(outpages);
+       if (cop){
+               if (cop->tfrm_op.indata) kfree(cop->tfrm_op.indata);
+               if (cop->tfrm_op.outdata) kfree(cop->tfrm_op.outdata);
+               kfree(cop);
+       }
+       if (jc) kfree(jc);
+
+       DEBUG(print_lock_status());
+
+       return err;
+}
+
+
+static int cryptocop_ioctl_create_session(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       cryptocop_session_id             sid;
+       int                              err;
+       struct cryptocop_private         *dev;
+       struct strcop_session_op         *sess_op = (struct strcop_session_op *)arg;
+       struct strcop_session_op         sop;
+       struct cryptocop_transform_init  *tis = NULL;
+       struct cryptocop_transform_init  ti_cipher = {0};
+       struct cryptocop_transform_init  ti_digest = {0};
+       struct cryptocop_transform_init  ti_csum = {0};
+
+       if (!access_ok(VERIFY_WRITE, sess_op, sizeof(struct strcop_session_op)))
+               return -EFAULT;
+       err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));
+       if (err) return -EFAULT;
+       if (sop.cipher != cryptocop_cipher_none) {
+               if (!access_ok(VERIFY_READ, sop.key, sop.keylen)) return -EFAULT;
+       }
+       DEBUG(printk("cryptocop_ioctl_create_session, sess_op:\n"));
+
+       DEBUG(printk("\tcipher:%d\n"
+                    "\tcipher_mode:%d\n"
+                    "\tdigest:%d\n"
+                    "\tcsum:%d\n",
+                    (int)sop.cipher,
+                    (int)sop.cmode,
+                    (int)sop.digest,
+                    (int)sop.csum));
+
+       if (sop.cipher != cryptocop_cipher_none){
+               /* Init the cipher. */
+               switch (sop.cipher){
+               case cryptocop_cipher_des:
+                       ti_cipher.alg = cryptocop_alg_des;
+                       break;
+               case cryptocop_cipher_3des:
+                       ti_cipher.alg = cryptocop_alg_3des;
+                       break;
+               case cryptocop_cipher_aes:
+                       ti_cipher.alg = cryptocop_alg_aes;
+                       break;
+               default:
+                       DEBUG_API(printk("create session, bad cipher algorithm %d\n", sop.cipher));
+                       return -EINVAL;
+               };
+               DEBUG(printk("setting cipher transform %d\n", ti_cipher.alg));
+               copy_from_user(ti_cipher.key, sop.key, sop.keylen/8);
+               ti_cipher.keylen = sop.keylen;
+               switch (sop.cmode){
+               case cryptocop_cipher_mode_cbc:
+               case cryptocop_cipher_mode_ecb:
+                       ti_cipher.cipher_mode = sop.cmode;
+                       break;
+               default:
+                       DEBUG_API(printk("create session, bad cipher mode %d\n", sop.cmode));
+                       return -EINVAL;
+               }
+               DEBUG(printk("cryptocop_ioctl_create_session: setting CBC mode %d\n", ti_cipher.cipher_mode));
+               switch (sop.des3_mode){
+               case cryptocop_3des_eee:
+               case cryptocop_3des_eed:
+               case cryptocop_3des_ede:
+               case cryptocop_3des_edd:
+               case cryptocop_3des_dee:
+               case cryptocop_3des_ded:
+               case cryptocop_3des_dde:
+               case cryptocop_3des_ddd:
+                       ti_cipher.tdes_mode = sop.des3_mode;
+                       break;
+               default:
+                       DEBUG_API(printk("create session, bad 3DES mode %d\n", sop.des3_mode));
+                       return -EINVAL;
+               }
+               ti_cipher.tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+               ti_cipher.next = tis;
+               tis = &ti_cipher;
+       } /* if (sop.cipher != cryptocop_cipher_none) */
+       if (sop.digest != cryptocop_digest_none){
+               DEBUG(printk("setting digest transform\n"));
+               switch (sop.digest){
+               case cryptocop_digest_md5:
+                       ti_digest.alg = cryptocop_alg_md5;
+                       break;
+               case cryptocop_digest_sha1:
+                       ti_digest.alg = cryptocop_alg_sha1;
+                       break;
+               default:
+                       DEBUG_API(printk("create session, bad digest algorithm %d\n", sop.digest));
+                       return -EINVAL;
+               }
+               ti_digest.tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+               ti_digest.next = tis;
+               tis = &ti_digest;
+       } /* if (sop.digest != cryptocop_digest_none) */
+       if (sop.csum != cryptocop_csum_none){
+               DEBUG(printk("setting csum transform\n"));
+               switch (sop.csum){
+               case cryptocop_csum_le:
+               case cryptocop_csum_be:
+                       ti_csum.csum_mode = sop.csum;
+                       break;
+               default:
+                       DEBUG_API(printk("create session, bad checksum algorithm %d\n", sop.csum));
+                       return -EINVAL;
+               }
+               ti_csum.alg = cryptocop_alg_csum;
+               ti_csum.tid = CRYPTOCOP_IOCTL_CSUM_TID;
+               ti_csum.next = tis;
+               tis = &ti_csum;
+       } /* (sop.csum != cryptocop_csum_none) */
+       dev = kmalloc(sizeof(struct cryptocop_private), GFP_KERNEL);
+       if (!dev){
+               DEBUG_API(printk("create session, alloc dev\n"));
+               return -ENOMEM;
+       }
+
+       err = cryptocop_new_session(&sid, tis, GFP_KERNEL);
+       DEBUG({ if (err) printk("create session, cryptocop_new_session %d\n", err);});
+
+       if (err) {
+               kfree(dev);
+               return err;
+       }
+       sess_op->ses_id = sid;
+       dev->sid = sid;
+       dev->next = filp->private_data;
+       filp->private_data = dev;
+
+       return 0;
+}
+
+static int cryptocop_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       int err = 0;
+       if (_IOC_TYPE(cmd) != ETRAXCRYPTOCOP_IOCTYPE) {
+               DEBUG_API(printk("cryptocop_ioctl: wrong type\n"));
+               return -ENOTTY;
+       }
+       if (_IOC_NR(cmd) > CRYPTOCOP_IO_MAXNR){
+               return -ENOTTY;
+       }
+       /* Access check of the argument.  Some commands, e.g. create session and process op,
+          needs additional checks.  Those are handled in the command handling functions. */
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
+       else if (_IOC_DIR(cmd) & _IOC_WRITE)
+               err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
+       if (err) return -EFAULT;
+
+       switch (cmd) {
+       case CRYPTOCOP_IO_CREATE_SESSION:
+               return cryptocop_ioctl_create_session(inode, filp, cmd, arg);
+       case CRYPTOCOP_IO_CLOSE_SESSION:
+               return cryptocop_ioctl_close_session(inode, filp, cmd, arg);
+       case CRYPTOCOP_IO_PROCESS_OP:
+               return cryptocop_ioctl_process(inode, filp, cmd, arg);
+       default:
+               DEBUG_API(printk("cryptocop_ioctl: unknown command\n"));
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+
+#ifdef LDEBUG
+static void print_dma_descriptors(struct cryptocop_int_operation *iop)
+{
+       struct cryptocop_dma_desc *cdesc_out = iop->cdesc_out;
+       struct cryptocop_dma_desc *cdesc_in = iop->cdesc_in;
+       int                       i;
+
+       printk("print_dma_descriptors start\n");
+
+       printk("iop:\n");
+       printk("\tsid: 0x%lld\n", iop->sid);
+
+       printk("\tcdesc_out: 0x%p\n", iop->cdesc_out);
+       printk("\tcdesc_in: 0x%p\n", iop->cdesc_in);
+       printk("\tddesc_out: 0x%p\n", iop->ddesc_out);
+       printk("\tddesc_in: 0x%p\n", iop->ddesc_in);
+
+       printk("\niop->ctx_out: 0x%p phys: 0x%p\n", &iop->ctx_out, (char*)virt_to_phys(&iop->ctx_out));
+       printk("\tnext: 0x%p\n"
+              "\tsaved_data: 0x%p\n"
+              "\tsaved_data_buf: 0x%p\n",
+              iop->ctx_out.next,
+              iop->ctx_out.saved_data,
+              iop->ctx_out.saved_data_buf);
+
+       printk("\niop->ctx_in: 0x%p phys: 0x%p\n", &iop->ctx_in, (char*)virt_to_phys(&iop->ctx_in));
+       printk("\tnext: 0x%p\n"
+              "\tsaved_data: 0x%p\n"
+              "\tsaved_data_buf: 0x%p\n",
+              iop->ctx_in.next,
+              iop->ctx_in.saved_data,
+              iop->ctx_in.saved_data_buf);
+
+       i = 0;
+       while (cdesc_out) {
+               dma_descr_data *td;
+               printk("cdesc_out %d, desc=0x%p\n", i, cdesc_out->dma_descr);
+               printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_out->dma_descr));
+               td = cdesc_out->dma_descr;
+               printk("\n\tbuf: 0x%p\n"
+                      "\tafter: 0x%p\n"
+                      "\tmd: 0x%04x\n"
+                      "\tnext: 0x%p\n",
+                      td->buf,
+                      td->after,
+                      td->md,
+                      td->next);
+               printk("flags:\n"
+                      "\twait:\t%d\n"
+                      "\teol:\t%d\n"
+                      "\touteop:\t%d\n"
+                      "\tineop:\t%d\n"
+                      "\tintr:\t%d\n",
+                      td->wait,
+                      td->eol,
+                      td->out_eop,
+                      td->in_eop,
+                      td->intr);
+               cdesc_out = cdesc_out->next;
+               i++;
+       }
+       i = 0;
+       while (cdesc_in) {
+               dma_descr_data *td;
+               printk("cdesc_in %d, desc=0x%p\n", i, cdesc_in->dma_descr);
+               printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_in->dma_descr));
+               td = cdesc_in->dma_descr;
+               printk("\n\tbuf: 0x%p\n"
+                      "\tafter: 0x%p\n"
+                      "\tmd: 0x%04x\n"
+                      "\tnext: 0x%p\n",
+                      td->buf,
+                      td->after,
+                      td->md,
+                      td->next);
+               printk("flags:\n"
+                      "\twait:\t%d\n"
+                      "\teol:\t%d\n"
+                      "\touteop:\t%d\n"
+                      "\tineop:\t%d\n"
+                      "\tintr:\t%d\n",
+                      td->wait,
+                      td->eol,
+                      td->out_eop,
+                      td->in_eop,
+                      td->intr);
+               cdesc_in = cdesc_in->next;
+               i++;
+       }
+
+       printk("print_dma_descriptors end\n");
+}
+
+
+static void print_strcop_crypto_op(struct strcop_crypto_op *cop)
+{
+       printk("print_strcop_crypto_op, 0x%p\n", cop);
+
+       /* Indata. */
+       printk("indata=0x%p\n"
+              "inlen=%d\n"
+              "do_cipher=%d\n"
+              "decrypt=%d\n"
+              "cipher_explicit=%d\n"
+              "cipher_start=%d\n"
+              "cipher_len=%d\n"
+              "outdata=0x%p\n"
+              "outlen=%d\n",
+              cop->indata,
+              cop->inlen,
+              cop->do_cipher,
+              cop->decrypt,
+              cop->cipher_explicit,
+              cop->cipher_start,
+              cop->cipher_len,
+              cop->cipher_outdata,
+              cop->cipher_outlen);
+
+       printk("do_digest=%d\n"
+              "digest_start=%d\n"
+              "digest_len=%d\n",
+              cop->do_digest,
+              cop->digest_start,
+              cop->digest_len);
+
+       printk("do_csum=%d\n"
+              "csum_start=%d\n"
+              "csum_len=%d\n",
+              cop->do_csum,
+              cop->csum_start,
+              cop->csum_len);
+}
+
+static void print_cryptocop_operation(struct cryptocop_operation *cop)
+{
+       struct cryptocop_desc      *d;
+       struct cryptocop_tfrm_cfg  *tc;
+       struct cryptocop_desc_cfg  *dc;
+       int                        i;
+
+       printk("print_cryptocop_operation, cop=0x%p\n\n", cop);
+       printk("sid: %lld\n", cop->sid);
+       printk("operation_status=%d\n"
+              "use_dmalists=%d\n"
+              "in_interrupt=%d\n"
+              "fast_callback=%d\n",
+              cop->operation_status,
+              cop->use_dmalists,
+              cop->in_interrupt,
+              cop->fast_callback);
+
+       if (cop->use_dmalists){
+               print_user_dma_lists(&cop->list_op);
+       } else {
+               printk("cop->tfrm_op\n"
+                      "tfrm_cfg=0x%p\n"
+                      "desc=0x%p\n"
+                      "indata=0x%p\n"
+                      "incount=%d\n"
+                      "inlen=%d\n"
+                      "outdata=0x%p\n"
+                      "outcount=%d\n"
+                      "outlen=%d\n\n",
+                      cop->tfrm_op.tfrm_cfg,
+                      cop->tfrm_op.desc,
+                      cop->tfrm_op.indata,
+                      cop->tfrm_op.incount,
+                      cop->tfrm_op.inlen,
+                      cop->tfrm_op.outdata,
+                      cop->tfrm_op.outcount,
+                      cop->tfrm_op.outlen);
+
+               tc = cop->tfrm_op.tfrm_cfg;
+               while (tc){
+                       printk("tfrm_cfg, 0x%p\n"
+                              "tid=%d\n"
+                              "flags=%d\n"
+                              "inject_ix=%d\n"
+                              "next=0x%p\n",
+                              tc,
+                              tc->tid,
+                              tc->flags,
+                              tc->inject_ix,
+                              tc->next);
+                       tc = tc->next;
+               }
+               d = cop->tfrm_op.desc;
+               while (d){
+                       printk("\n======================desc, 0x%p\n"
+                              "length=%d\n"
+                              "cfg=0x%p\n"
+                              "next=0x%p\n",
+                              d,
+                              d->length,
+                              d->cfg,
+                              d->next);
+                       dc = d->cfg;
+                       while (dc){
+                               printk("=========desc_cfg, 0x%p\n"
+                                      "tid=%d\n"
+                                      "src=%d\n"
+                                      "last=%d\n"
+                                      "next=0x%p\n",
+                                      dc,
+                                      dc->tid,
+                                      dc->src,
+                                      dc->last,
+                                      dc->next);
+                               dc = dc->next;
+                       }
+                       d = d->next;
+               }
+               printk("\n====iniov\n");
+               for (i = 0; i < cop->tfrm_op.incount; i++){
+                       printk("indata[%d]\n"
+                              "base=0x%p\n"
+                              "len=%d\n",
+                              i,
+                              cop->tfrm_op.indata[i].iov_base,
+                              cop->tfrm_op.indata[i].iov_len);
+               }
+               printk("\n====outiov\n");
+               for (i = 0; i < cop->tfrm_op.outcount; i++){
+                       printk("outdata[%d]\n"
+                              "base=0x%p\n"
+                              "len=%d\n",
+                              i,
+                              cop->tfrm_op.outdata[i].iov_base,
+                              cop->tfrm_op.outdata[i].iov_len);
+               }
+       }
+       printk("------------end print_cryptocop_operation\n");
+}
+
+
+static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op)
+{
+       dma_descr_data *dd;
+       int i;
+
+       printk("print_user_dma_lists, dma_op=0x%p\n", dma_op);
+
+       printk("out_data_buf = 0x%p, phys_to_virt(out_data_buf) = 0x%p\n", dma_op->out_data_buf, phys_to_virt((unsigned long int)dma_op->out_data_buf));
+       printk("in_data_buf = 0x%p, phys_to_virt(in_data_buf) = 0x%p\n", dma_op->in_data_buf, phys_to_virt((unsigned long int)dma_op->in_data_buf));
+
+       printk("##############outlist\n");
+       dd = phys_to_virt((unsigned long int)dma_op->outlist);
+       i = 0;
+       while (dd != NULL) {
+               printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);
+               printk("\n\tbuf: 0x%p\n"
+                      "\tafter: 0x%p\n"
+                      "\tmd: 0x%04x\n"
+                      "\tnext: 0x%p\n",
+                      dd->buf,
+                      dd->after,
+                      dd->md,
+                      dd->next);
+               printk("flags:\n"
+                      "\twait:\t%d\n"
+                      "\teol:\t%d\n"
+                      "\touteop:\t%d\n"
+                      "\tineop:\t%d\n"
+                      "\tintr:\t%d\n",
+                      dd->wait,
+                      dd->eol,
+                      dd->out_eop,
+                      dd->in_eop,
+                      dd->intr);
+               if (dd->eol)
+                       dd = NULL;
+               else
+                       dd = phys_to_virt((unsigned long int)dd->next);
+               ++i;
+       }
+
+       printk("##############inlist\n");
+       dd = phys_to_virt((unsigned long int)dma_op->inlist);
+       i = 0;
+       while (dd != NULL) {
+               printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);
+               printk("\n\tbuf: 0x%p\n"
+                      "\tafter: 0x%p\n"
+                      "\tmd: 0x%04x\n"
+                      "\tnext: 0x%p\n",
+                      dd->buf,
+                      dd->after,
+                      dd->md,
+                      dd->next);
+               printk("flags:\n"
+                      "\twait:\t%d\n"
+                      "\teol:\t%d\n"
+                      "\touteop:\t%d\n"
+                      "\tineop:\t%d\n"
+                      "\tintr:\t%d\n",
+                      dd->wait,
+                      dd->eol,
+                      dd->out_eop,
+                      dd->in_eop,
+                      dd->intr);
+               if (dd->eol)
+                       dd = NULL;
+               else
+                       dd = phys_to_virt((unsigned long int)dd->next);
+               ++i;
+       }
+}
+
+
+static void print_lock_status(void)
+{
+       printk("**********************print_lock_status\n");
+       printk("cryptocop_completed_jobs_lock %d\n", spin_is_locked(&cryptocop_completed_jobs_lock));
+       printk("cryptocop_job_queue_lock %d\n", spin_is_locked(&cryptocop_job_queue_lock));
+       printk("descr_pool_lock %d\n", spin_is_locked(&descr_pool_lock));
+       printk("cryptocop_sessions_lock %d\n", spin_is_locked(cryptocop_sessions_lock));
+       printk("running_job_lock %d\n", spin_is_locked(running_job_lock));
+       printk("cryptocop_process_lock %d\n", spin_is_locked(cryptocop_process_lock));
+}
+#endif /* LDEBUG */
+
+
+static const char cryptocop_name[] = "ETRAX FS stream co-processor";
+
+static int init_stream_coprocessor(void)
+{
+       int err;
+       int i;
+       static int initialized = 0;
+
+       if (initialized)
+               return 0;
+
+       initialized = 1;
+
+       printk("ETRAX FS stream co-processor driver v0.01, (c) 2003 Axis Communications AB\n");
+
+       err = register_chrdev(CRYPTOCOP_MAJOR, cryptocop_name, &cryptocop_fops);
+       if (err < 0) {
+               printk(KERN_ERR "stream co-processor: could not get major number.\n");
+               return err;
+       }
+
+       err = init_cryptocop();
+       if (err) {
+               (void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);
+               return err;
+       }
+       err = cryptocop_job_queue_init();
+       if (err) {
+               release_cryptocop();
+               (void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);
+               return err;
+       }
+       /* Init the descriptor pool. */
+       for (i = 0; i < CRYPTOCOP_DESCRIPTOR_POOL_SIZE - 1; i++) {
+               descr_pool[i].from_pool = 1;
+               descr_pool[i].next = &descr_pool[i + 1];
+       }
+       descr_pool[i].from_pool = 1;
+       descr_pool[i].next = NULL;
+       descr_pool_free_list = &descr_pool[0];
+       descr_pool_no_free = CRYPTOCOP_DESCRIPTOR_POOL_SIZE;
+
+       spin_lock_init(&cryptocop_completed_jobs_lock);
+       spin_lock_init(&cryptocop_job_queue_lock);
+       spin_lock_init(&descr_pool_lock);
+       spin_lock_init(&cryptocop_sessions_lock);
+       spin_lock_init(&running_job_lock);
+       spin_lock_init(&cryptocop_process_lock);
+
+       cryptocop_sessions = NULL;
+       next_sid = 1;
+
+       cryptocop_running_job = NULL;
+
+       printk("stream co-processor: init done.\n");
+       return 0;
+}
+
+static void __exit exit_stream_coprocessor(void)
+{
+       release_cryptocop();
+       cryptocop_job_queue_close();
+}
+
+module_init(init_stream_coprocessor);
+module_exit(exit_stream_coprocessor);
+
diff --git a/arch/cris/arch-v32/drivers/gpio.c b/arch/cris/arch-v32/drivers/gpio.c
new file mode 100644 (file)
index 0000000..a551237
--- /dev/null
@@ -0,0 +1,766 @@
+/* $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
+ *
+ * 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 <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <asm/etraxgpio.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/gio_defs.h>
+#include <asm/arch/hwregs/intr_vect_defs.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+/* The following gio ports on ETRAX FS is available:
+ * pa  8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge
+ * pb 18 bits
+ * pc 18 bits
+ * pd 18 bits
+ * pe 18 bits
+ * each port has a rw_px_dout, r_px_din and rw_px_oe register.
+ */
+
+#define GPIO_MAJOR 120  /* experimental MAJOR number */
+
+#define D(x)
+
+#if 0
+static int dp_cnt;
+#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
+#else
+#define DP(x)
+#endif
+
+static char gpio_name[] = "etrax gpio";
+
+#if 0
+static wait_queue_head_t *gpio_wq;
+#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,
+                          loff_t *off);
+static int gpio_open(struct inode *inode, struct file *filp);
+static int gpio_release(struct inode *inode, struct file *filp);
+static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
+
+/* private data per open() of this driver */
+
+struct gpio_private {
+       struct gpio_private *next;
+       /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
+       unsigned char clk_mask;
+       unsigned char data_mask;
+       unsigned char write_msb;
+       unsigned char pad1;
+       /* These fields are generic */
+       unsigned long highalarm, lowalarm;
+       wait_queue_head_t alarm_wq;
+       int minor;
+};
+
+/* linked list of alarms to check for */
+
+static struct gpio_private *alarmlist = 0;
+
+static int gpio_some_alarms = 0; /* Set if someone uses alarm */
+static unsigned long gpio_pa_high_alarms = 0;
+static unsigned long gpio_pa_low_alarms = 0;
+
+static DEFINE_SPINLOCK(alarm_lock);
+
+#define NUM_PORTS (GPIO_MINOR_LAST+1)
+#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;
+
+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_pe_dout),
+};
+
+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_pe_din),
+};
+
+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_PE_CHANGEABLE_DIR,
+};
+
+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,
+};
+
+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_pe_oe),
+};
+
+
+
+static unsigned int
+gpio_poll(struct file *file,
+         poll_table *wait)
+{
+       unsigned int mask = 0;
+       struct gpio_private *priv = (struct gpio_private *)file->private_data;
+       unsigned long data;
+       poll_wait(file, &priv->alarm_wq, wait);
+       if (priv->minor == GPIO_MINOR_A) {
+               reg_gio_rw_intr_cfg intr_cfg;
+               unsigned long tmp;
+               unsigned long flags;
+
+               local_irq_save(flags);
+               data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din));
+               /* PA has support for interrupt
+                * lets activate high for those low and with highalarm set
+                */
+               intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
+
+               tmp = ~data & priv->highalarm & 0xFF;
+                if (tmp & (1 << 0)) {
+                       intr_cfg.pa0 = regk_gio_hi;
+               }
+                if (tmp & (1 << 1)) {
+                       intr_cfg.pa1 = regk_gio_hi;
+               }
+                if (tmp & (1 << 2)) {
+                       intr_cfg.pa2 = regk_gio_hi;
+               }
+                if (tmp & (1 << 3)) {
+                       intr_cfg.pa3 = regk_gio_hi;
+               }
+                if (tmp & (1 << 4)) {
+                       intr_cfg.pa4 = regk_gio_hi;
+               }
+                if (tmp & (1 << 5)) {
+                       intr_cfg.pa5 = regk_gio_hi;
+               }
+                if (tmp & (1 << 6)) {
+                       intr_cfg.pa6 = regk_gio_hi;
+               }
+                if (tmp & (1 << 7)) {
+                       intr_cfg.pa7 = regk_gio_hi;
+               }
+               /*
+                * lets activate low for those high and with lowalarm set
+                */
+               tmp = data & priv->lowalarm & 0xFF;
+                if (tmp & (1 << 0)) {
+                       intr_cfg.pa0 = regk_gio_lo;
+               }
+                if (tmp & (1 << 1)) {
+                       intr_cfg.pa1 = regk_gio_lo;
+               }
+                if (tmp & (1 << 2)) {
+                       intr_cfg.pa2 = regk_gio_lo;
+               }
+                if (tmp & (1 << 3)) {
+                       intr_cfg.pa3 = regk_gio_lo;
+               }
+                if (tmp & (1 << 4)) {
+                       intr_cfg.pa4 = regk_gio_lo;
+               }
+                if (tmp & (1 << 5)) {
+                       intr_cfg.pa5 = regk_gio_lo;
+               }
+                if (tmp & (1 << 6)) {
+                       intr_cfg.pa6 = regk_gio_lo;
+               }
+                if (tmp & (1 << 7)) {
+                       intr_cfg.pa7 = regk_gio_lo;
+               }
+
+               REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
+               local_irq_restore(flags);
+       } else if (priv->minor <= GPIO_MINOR_E)
+               data = *data_in[priv->minor];
+       else
+               return 0;
+
+       if ((data & priv->highalarm) ||
+           (~data & priv->lowalarm)) {
+               mask = POLLIN|POLLRDNORM;
+       }
+
+       DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
+       return mask;
+}
+
+int etrax_gpio_wake_up_check(void)
+{
+       struct gpio_private *priv = alarmlist;
+       unsigned long data = 0;
+        int ret = 0;
+       while (priv) {
+               data = *data_in[priv->minor];
+               if ((data & priv->highalarm) ||
+                   (~data & priv->lowalarm)) {
+                       DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
+                       wake_up_interruptible(&priv->alarm_wq);
+                        ret = 1;
+               }
+               priv = priv->next;
+       }
+        return ret;
+}
+
+static irqreturn_t
+gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (gpio_some_alarms) {
+               return IRQ_RETVAL(etrax_gpio_wake_up_check());
+       }
+        return IRQ_NONE;
+}
+
+static irqreturn_t
+gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       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;
+
+       /* 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);
+
+       /* Find those that we have enabled */
+       spin_lock(&alarm_lock);
+       tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms);
+       spin_unlock(&alarm_lock);
+
+       /* 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);
+
+       /* Disable those interrupts.. */
+       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;
+       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;
+}
+
+
+static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+                          loff_t *off)
+{
+       struct gpio_private *priv = (struct gpio_private *)file->private_data;
+       unsigned char data, clk_mask, data_mask, write_msb;
+       unsigned long flags;
+       unsigned long shadow;
+       volatile unsigned long *port;
+       ssize_t retval = count;
+       /* Only bits 0-7 may be used for write operations but allow all
+          devices except leds... */
+       if (priv->minor == GPIO_MINOR_LEDS) {
+               return -EFAULT;
+       }
+
+       if (!access_ok(VERIFY_READ, buf, count)) {
+               return -EFAULT;
+       }
+       clk_mask = priv->clk_mask;
+       data_mask = priv->data_mask;
+       /* It must have been configured using the IO_CFG_WRITE_MODE */
+       /* Perhaps a better error code? */
+       if (clk_mask == 0 || data_mask == 0) {
+               return -EPERM;
+       }
+       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));
+       port = data_out[priv->minor];
+
+       while (count--) {
+               int i;
+               data = *buf++;
+               if (priv->write_msb) {
+                       for (i = 7; i >= 0;i--) {
+                               local_irq_save(flags);
+                               shadow = *port;
+                               *port = shadow &= ~clk_mask;
+                               if (data & 1<<i)
+                                       *port = shadow |= data_mask;
+                               else
+                                       *port = shadow &= ~data_mask;
+                       /* For FPGA: min 5.0ns (DCC) before CCLK high */
+                               *port = shadow |= clk_mask;
+                               local_irq_restore(flags);
+                       }
+               } else {
+                       for (i = 0; i <= 7;i++) {
+                               local_irq_save(flags);
+                               shadow = *port;
+                               *port = shadow &= ~clk_mask;
+                               if (data & 1<<i)
+                                       *port = shadow |= data_mask;
+                               else
+                                       *port = shadow &= ~data_mask;
+                       /* For FPGA: min 5.0ns (DCC) before CCLK high */
+                               *port = shadow |= clk_mask;
+                               local_irq_restore(flags);
+                       }
+               }
+       }
+       return retval;
+}
+
+
+
+static int
+gpio_open(struct inode *inode, struct file *filp)
+{
+       struct gpio_private *priv;
+       int p = MINOR(inode->i_rdev);
+
+       if (p > GPIO_MINOR_LAST)
+               return -EINVAL;
+
+       priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
+                                             GFP_KERNEL);
+
+       if (!priv)
+               return -ENOMEM;
+
+       priv->minor = p;
+
+       /* initialize the io/alarm struct and link it into our alarmlist */
+
+       priv->next = alarmlist;
+       alarmlist = priv;
+       priv->clk_mask = 0;
+       priv->data_mask = 0;
+       priv->highalarm = 0;
+       priv->lowalarm = 0;
+       init_waitqueue_head(&priv->alarm_wq);
+
+       filp->private_data = (void *)priv;
+
+       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;
+       /* local copies while updating them: */
+       unsigned long a_high, a_low;
+       unsigned long some_alarms;
+
+       /* unlink from alarmlist and free the private structure */
+
+       if (p == todel) {
+               alarmlist = todel->next;
+       } else {
+               while (p->next != todel)
+                       p = p->next;
+               p->next = todel->next;
+       }
+
+       kfree(todel);
+       /* Check if there are still any alarms set */
+       p = alarmlist;
+        some_alarms = 0;
+       a_high = 0;
+       a_low = 0;
+       while (p) {
+               if (p->minor == GPIO_MINOR_A) {
+                       a_high |= p->highalarm;
+                       a_low |= p->lowalarm;
+               }
+
+               if (p->highalarm | p->lowalarm) {
+                       some_alarms = 1;
+               }
+               p = p->next;
+       }
+
+       spin_lock(&alarm_lock);
+       gpio_some_alarms = some_alarms;
+       gpio_pa_high_alarms = a_high;
+       gpio_pa_low_alarms = a_low;
+       spin_unlock(&alarm_lock);
+
+       return 0;
+}
+
+/* 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
+        */
+       unsigned long flags;
+       unsigned long dir_shadow;
+
+       local_irq_save(flags);
+       dir_shadow = *dir_oe[priv->minor];
+       dir_shadow &= ~(arg & changeable_dir[priv->minor]);
+       *dir_oe[priv->minor] = dir_shadow;
+       local_irq_restore(flags);
+
+       if (priv->minor == GPIO_MINOR_A)
+               dir_shadow ^= 0xFF;    /* Only 8 bits */
+       else
+               dir_shadow ^= 0x3FFFF; /* Only 18 bits */
+       return dir_shadow;
+
+} /* setget_input */
+
+unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
+{
+       unsigned long flags;
+       unsigned long dir_shadow;
+
+       local_irq_save(flags);
+       dir_shadow = *dir_oe[priv->minor];
+       dir_shadow |=  (arg & changeable_dir[priv->minor]);
+       *dir_oe[priv->minor] = dir_shadow;
+       local_irq_restore(flags);
+       return dir_shadow;
+} /* setget_output */
+
+static int
+gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
+
+static int
+gpio_ioctl(struct inode *inode, struct file *file,
+          unsigned int cmd, unsigned long arg)
+{
+       unsigned long flags;
+       unsigned long val;
+       unsigned long shadow;
+       struct gpio_private *priv = (struct gpio_private *)file->private_data;
+       if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+               return -EINVAL;
+       }
+
+       switch (_IOC_NR(cmd)) {
+       case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
+               // read the port
+               return *data_in[priv->minor];
+               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]);
+               *data_out[priv->minor] = shadow;
+               local_irq_restore(flags);
+               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]);
+               *data_out[priv->minor] = 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;
+               if (priv->minor == GPIO_MINOR_A) {
+                       gpio_pa_high_alarms |= arg;
+               }
+               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;
+               if (priv->minor == GPIO_MINOR_A) {
+                       gpio_pa_low_alarms |= arg;
+               }
+               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);
+               if (priv->minor == GPIO_MINOR_A) {
+                       if (gpio_pa_high_alarms & arg ||
+                           gpio_pa_low_alarms & arg) {
+                               /* Must update the gpio_pa_*alarms masks */
+                       }
+               }
+               spin_unlock(&alarm_lock);
+               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
+                */
+               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
+                */
+               return setget_output(priv, arg);
+
+       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 = *data_in[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 */
+               val = *data_out[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.
+                */
+               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;
+               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:
+               if (priv->minor == GPIO_MINOR_LEDS)
+                       return gpio_leds_ioctl(cmd, arg);
+               else
+                       return -EINVAL;
+       } /* switch */
+
+       return 0;
+}
+
+static int
+gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
+{
+       unsigned char green;
+       unsigned char red;
+
+       switch (_IOC_NR(cmd)) {
+       case IO_LEDACTIVE_SET:
+               green = ((unsigned char) arg) & 1;
+               red   = (((unsigned char) arg) >> 1) & 1;
+               LED_ACTIVE_SET_G(green);
+               LED_ACTIVE_SET_R(red);
+               break;
+
+       default:
+               return -EINVAL;
+       } /* switch */
+
+       return 0;
+}
+
+struct file_operations gpio_fops = {
+       .owner       = THIS_MODULE,
+       .poll        = gpio_poll,
+       .ioctl       = gpio_ioctl,
+       .write       = gpio_write,
+       .open        = gpio_open,
+       .release     = gpio_release,
+};
+
+
+/* main driver initialization routine, called from mem.c */
+
+static __init int
+gpio_init(void)
+{
+       int res;
+       reg_intr_vect_rw_mask intr_mask;
+
+       /* do the formalities */
+
+       res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
+       if (res < 0) {
+               printk(KERN_ERR "gpio: couldn't get a major number.\n");
+               return res;
+       }
+
+       /* Clear all leds */
+       LED_NETWORK_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");
+       /* 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,
+                       SA_SHIRQ | SA_INTERRUPT,"gpio poll", &alarmlist)) {
+               printk("err: timer0 irq for gpio\n");
+       }
+       if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt,
+                       SA_SHIRQ | SA_INTERRUPT,"gpio PA", &alarmlist)) {
+               printk("err: PA irq for gpio\n");
+       }
+       /* enable the gio and timer irq in global config */
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.timer = 1;
+       intr_mask.gen_io = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+       return res;
+}
+
+/* this makes sure that gpio_init is called during kernel boot */
+
+module_init(gpio_init);
diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c
new file mode 100644 (file)
index 0000000..440c20a
--- /dev/null
@@ -0,0 +1,611 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : i2c.c
+*!
+*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
+*!              kernel modules (i2c_writereg/readreg) and from userspace using
+*!              ioctl()'s
+*!
+*! Nov 30 1998  Torbjorn Eliasson  Initial version.
+*!              Bjorn Wesen        Elinux kernel version.
+*! 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
+*!
+*! ---------------------------------------------------------------------------
+*!
+*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
+*!
+*!***************************************************************************/
+/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
+/****************** INCLUDE FILES SECTION ***********************************/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <asm/etraxi2c.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "i2c.h"
+
+/****************** I2C DEFINITION SECTION *************************/
+
+#define D(x)
+
+#define I2C_MAJOR 123  /* LOCAL/EXPERIMENTAL */
+static const char i2c_name[] = "i2c";
+
+#define CLOCK_LOW_TIME            8
+#define CLOCK_HIGH_TIME           8
+#define START_CONDITION_HOLD_TIME 8
+#define STOP_CONDITION_HOLD_TIME  8
+#define ENABLE_OUTPUT 0x01
+#define ENABLE_INPUT 0x00
+#define I2C_CLOCK_HIGH 1
+#define I2C_CLOCK_LOW 0
+#define I2C_DATA_HIGH 1
+#define I2C_DATA_LOW 0
+
+#define i2c_enable()
+#define i2c_disable()
+
+/* enable or disable output-enable, to select output or input on the i2c bus */
+
+#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
+#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
+
+/* control the i2c clock and data signals */
+
+#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
+#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
+
+/* read a bit from the i2c interface */
+
+#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
+
+#define i2c_delay(usecs) udelay(usecs)
+
+/****************** VARIABLE SECTION ************************************/
+
+static struct crisv32_iopin cris_i2c_clk;
+static struct crisv32_iopin cris_i2c_data;
+
+/****************** FUNCTION DEFINITION SECTION *************************/
+
+
+/* generate i2c start condition */
+
+void
+i2c_start(void)
+{
+       /*
+        * SCL=1 SDA=1
+        */
+       i2c_dir_out();
+       i2c_delay(CLOCK_HIGH_TIME/6);
+       i2c_data(I2C_DATA_HIGH);
+       i2c_clk(I2C_CLOCK_HIGH);
+       i2c_delay(CLOCK_HIGH_TIME);
+       /*
+        * SCL=1 SDA=0
+        */
+       i2c_data(I2C_DATA_LOW);
+       i2c_delay(START_CONDITION_HOLD_TIME);
+       /*
+        * SCL=0 SDA=0
+        */
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_LOW_TIME);
+}
+
+/* generate i2c stop condition */
+
+void
+i2c_stop(void)
+{
+       i2c_dir_out();
+
+       /*
+        * SCL=0 SDA=0
+        */
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_data(I2C_DATA_LOW);
+       i2c_delay(CLOCK_LOW_TIME*2);
+       /*
+        * SCL=1 SDA=0
+        */
+       i2c_clk(I2C_CLOCK_HIGH);
+       i2c_delay(CLOCK_HIGH_TIME*2);
+       /*
+        * SCL=1 SDA=1
+        */
+       i2c_data(I2C_DATA_HIGH);
+       i2c_delay(STOP_CONDITION_HOLD_TIME);
+
+       i2c_dir_in();
+}
+
+/* write a byte to the i2c interface */
+
+void
+i2c_outbyte(unsigned char x)
+{
+       int i;
+
+       i2c_dir_out();
+
+       for (i = 0; i < 8; i++) {
+               if (x & 0x80) {
+                       i2c_data(I2C_DATA_HIGH);
+               } else {
+                       i2c_data(I2C_DATA_LOW);
+               }
+
+               i2c_delay(CLOCK_LOW_TIME/2);
+               i2c_clk(I2C_CLOCK_HIGH);
+               i2c_delay(CLOCK_HIGH_TIME);
+               i2c_clk(I2C_CLOCK_LOW);
+               i2c_delay(CLOCK_LOW_TIME/2);
+               x <<= 1;
+       }
+       i2c_data(I2C_DATA_LOW);
+       i2c_delay(CLOCK_LOW_TIME/2);
+
+       /*
+        * enable input
+        */
+       i2c_dir_in();
+}
+
+/* read a byte from the i2c interface */
+
+unsigned char
+i2c_inbyte(void)
+{
+       unsigned char aBitByte = 0;
+       int i;
+
+       /* Switch off I2C to get bit */
+       i2c_disable();
+       i2c_dir_in();
+       i2c_delay(CLOCK_HIGH_TIME/2);
+
+       /* Get bit */
+       aBitByte |= i2c_getbit();
+
+       /* Enable I2C */
+       i2c_enable();
+       i2c_delay(CLOCK_LOW_TIME/2);
+
+       for (i = 1; i < 8; i++) {
+               aBitByte <<= 1;
+               /* Clock pulse */
+               i2c_clk(I2C_CLOCK_HIGH);
+               i2c_delay(CLOCK_HIGH_TIME);
+               i2c_clk(I2C_CLOCK_LOW);
+               i2c_delay(CLOCK_LOW_TIME);
+
+               /* Switch off I2C to get bit */
+               i2c_disable();
+               i2c_dir_in();
+               i2c_delay(CLOCK_HIGH_TIME/2);
+
+               /* Get bit */
+               aBitByte |= i2c_getbit();
+
+               /* Enable I2C */
+               i2c_enable();
+               i2c_delay(CLOCK_LOW_TIME/2);
+       }
+       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
+        */
+       i2c_clk(I2C_CLOCK_LOW);
+       return aBitByte;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_getack
+*#
+*# DESCRIPTION  : checks if ack was received from ic2
+*#
+*#--------------------------------------------------------------------------*/
+
+int
+i2c_getack(void)
+{
+       int ack = 1;
+       /*
+        * enable output
+        */
+       i2c_dir_out();
+       /*
+        * Release data bus by setting
+        * data high
+        */
+       i2c_data(I2C_DATA_HIGH);
+       /*
+        * enable input
+        */
+       i2c_dir_in();
+       i2c_delay(CLOCK_HIGH_TIME/4);
+       /*
+        * generate ACK clock pulse
+        */
+       i2c_clk(I2C_CLOCK_HIGH);
+       /*
+        * Use PORT PB instead of I2C
+        * for input. (I2C not working)
+        */
+       i2c_clk(1);
+       i2c_data(1);
+       /*
+        * switch off I2C
+        */
+       i2c_data(1);
+       i2c_disable();
+       i2c_dir_in();
+       /*
+        * now wait for ack
+        */
+       i2c_delay(CLOCK_HIGH_TIME/2);
+       /*
+        * check for ack
+        */
+       if(i2c_getbit())
+               ack = 0;
+       i2c_delay(CLOCK_HIGH_TIME/2);
+       if(!ack){
+               if(!i2c_getbit()) /* receiver pulld SDA low */
+                       ack = 1;
+               i2c_delay(CLOCK_HIGH_TIME/2);
+       }
+
+   /*
+    * our clock is high now, make sure data is low
+    * before we enable our output. If we keep data high
+    * and enable output, we would generate a stop condition.
+    */
+   i2c_data(I2C_DATA_LOW);
+
+       /*
+        * end clock pulse
+        */
+       i2c_enable();
+       i2c_dir_out();
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_HIGH_TIME/4);
+       /*
+        * enable output
+        */
+       i2c_dir_out();
+       /*
+        * remove ACK clock pulse
+        */
+       i2c_data(I2C_DATA_HIGH);
+       i2c_delay(CLOCK_LOW_TIME/2);
+       return ack;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: I2C::sendAck
+*#
+*# DESCRIPTION  : Send ACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendack(void)
+{
+       /*
+        * enable output
+        */
+       i2c_delay(CLOCK_LOW_TIME);
+       i2c_dir_out();
+       /*
+        * set ack pulse high
+        */
+       i2c_data(I2C_DATA_LOW);
+       /*
+        * generate clock pulse
+        */
+       i2c_delay(CLOCK_HIGH_TIME/6);
+       i2c_clk(I2C_CLOCK_HIGH);
+       i2c_delay(CLOCK_HIGH_TIME);
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_LOW_TIME/6);
+       /*
+        * reset data out
+        */
+       i2c_data(I2C_DATA_HIGH);
+       i2c_delay(CLOCK_LOW_TIME);
+
+       i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_sendnack
+*#
+*# DESCRIPTION  : Sends NACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendnack(void)
+{
+       /*
+        * enable output
+        */
+       i2c_delay(CLOCK_LOW_TIME);
+       i2c_dir_out();
+       /*
+        * set data high
+        */
+       i2c_data(I2C_DATA_HIGH);
+       /*
+        * generate clock pulse
+        */
+       i2c_delay(CLOCK_HIGH_TIME/6);
+       i2c_clk(I2C_CLOCK_HIGH);
+       i2c_delay(CLOCK_HIGH_TIME);
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_LOW_TIME);
+
+       i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_writereg
+*#
+*# DESCRIPTION  : Writes a value to an I2C device
+*#
+*#--------------------------------------------------------------------------*/
+int
+i2c_writereg(unsigned char theSlave, unsigned char theReg,
+            unsigned char theValue)
+{
+       int error, cntr = 3;
+       unsigned long flags;
+
+       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;
+               /*
+                * now select register
+                */
+               i2c_dir_out();
+               i2c_outbyte(theReg);
+               /*
+                * now it's time to wait for ack
+                */
+               if(!i2c_getack())
+                       error |= 2;
+               /*
+                * send register register data
+                */
+               i2c_outbyte(theValue);
+               /*
+                * 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);
+
+       return -error;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_readreg
+*#
+*# DESCRIPTION  : Reads a value from the decoder registers.
+*#
+*#--------------------------------------------------------------------------*/
+unsigned char
+i2c_readreg(unsigned char theSlave, unsigned char theReg)
+{
+       unsigned char b = 0;
+       int error, cntr = 3;
+       unsigned long flags;
+
+       do {
+               error = 0;
+               /*
+                * we don't like to be interrupted
+                */
+                local_irq_save(flags);
+               /*
+                * generate start condition
+                */
+               i2c_start();
+
+               /*
+                * send slave address
+                */
+               i2c_outbyte((theSlave & 0xfe));
+               /*
+                * wait for ack
+                */
+               if(!i2c_getack())
+                       error = 1;
+               /*
+                * now select register
+                */
+               i2c_dir_out();
+               i2c_outbyte(theReg);
+               /*
+                * now it's time to wait for ack
+                */
+               if(!i2c_getack())
+                       error = 1;
+               /*
+                * repeat start condition
+                */
+               i2c_delay(CLOCK_LOW_TIME);
+               i2c_start();
+               /*
+                * send slave address
+                */
+               i2c_outbyte(theSlave | 0x01);
+               /*
+                * wait for ack
+                */
+               if(!i2c_getack())
+                       error = 1;
+               /*
+                * fetch register
+                */
+               b = i2c_inbyte();
+               /*
+                * 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--);
+
+       return b;
+}
+
+static int
+i2c_open(struct inode *inode, struct file *filp)
+{
+       return 0;
+}
+
+static int
+i2c_release(struct inode *inode, struct file *filp)
+{
+       return 0;
+}
+
+/* Main device API. ioctl's to write or read to/from i2c registers.
+ */
+
+static int
+i2c_ioctl(struct inode *inode, struct file *file,
+         unsigned int cmd, unsigned long arg)
+{
+       if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
+               return -EINVAL;
+       }
+
+       switch (_IOC_NR(cmd)) {
+               case I2C_WRITEREG:
+                       /* write to an i2c slave */
+                       D(printk("i2cw %d %d %d\n",
+                                I2C_ARGSLAVE(arg),
+                                I2C_ARGREG(arg),
+                                I2C_ARGVALUE(arg)));
+
+                       return i2c_writereg(I2C_ARGSLAVE(arg),
+                                           I2C_ARGREG(arg),
+                                           I2C_ARGVALUE(arg));
+               case I2C_READREG:
+               {
+                       unsigned char val;
+                       /* read from an i2c slave */
+                       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;
+}
+
+static struct file_operations i2c_fops = {
+       owner:    THIS_MODULE,
+       ioctl:    i2c_ioctl,
+       open:     i2c_open,
+       release:  i2c_release,
+};
+
+int __init
+i2c_init(void)
+{
+       int res;
+
+       /* Setup and enable the Port B I2C interface */
+
+        crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
+        crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
+
+       /* 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");
+
+       return 0;
+}
+
+/* this makes sure that i2c_init is called during boot */
+
+module_init(i2c_init);
+
+/****************** END OF FILE i2c.c ********************************/
diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h
new file mode 100644 (file)
index 0000000..bfe1a13
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include <linux/init.h>
+
+/* High level I2C actions */
+int __init i2c_init(void);
+int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
+unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
+
+/* Low level I2C */
+void i2c_start(void);
+void i2c_stop(void);
+void i2c_outbyte(unsigned char x);
+unsigned char i2c_inbyte(void);
+int i2c_getack(void);
+void i2c_sendack(void);
diff --git a/arch/cris/arch-v32/drivers/iop_fw_load.c b/arch/cris/arch-v32/drivers/iop_fw_load.c
new file mode 100644 (file)
index 0000000..11f9895
--- /dev/null
@@ -0,0 +1,219 @@
+/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $
+ *
+ * Firmware loader for ETRAX FS IO-Processor
+ *
+ * Copyright (C) 2004  Axis Communications AB
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/iop/iop_reg_space.h>
+#include <asm/arch/hwregs/iop/iop_mpu_macros.h>
+#include <asm/arch/hwregs/iop/iop_mpu_defs.h>
+#include <asm/arch/hwregs/iop/iop_spu_defs.h>
+#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h>
+
+#define IOP_TIMEOUT 100
+
+static struct device iop_spu_device[2] = {
+       { .bus_id =     "iop-spu0", },
+       { .bus_id =     "iop-spu1", },
+};
+
+static struct device iop_mpu_device = {
+       .bus_id =       "iop-mpu",
+};
+
+static int wait_mpu_idle(void)
+{
+       reg_iop_mpu_r_stat mpu_stat;
+       unsigned int timeout = IOP_TIMEOUT;
+
+       do {
+               mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
+       } while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
+       if (timeout == 0) {
+               printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
+{
+       reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
+               .wr_spu0_mem =    regk_iop_sw_cpu_no,
+               .wr_spu1_mem =    regk_iop_sw_cpu_no,
+               .size =           4,
+               .cmd =            regk_iop_sw_cpu_reg_copy,
+               .keep_owner =     regk_iop_sw_cpu_yes
+       };
+       reg_iop_spu_rw_ctrl spu_ctrl = {
+               .en  =            regk_iop_spu_no,
+               .fsm =            regk_iop_spu_no,
+       };
+       reg_iop_sw_cpu_r_mc_stat mc_stat;
+        const struct firmware *fw_entry;
+       u32 *data;
+       unsigned int timeout;
+       int retval, i;
+
+       if (spu_inst > 1)
+               return -ENODEV;
+
+       /* get firmware */
+       retval = request_firmware(&fw_entry,
+                                 fw_name,
+                                 &iop_spu_device[spu_inst]);
+       if (retval != 0)
+       {
+               printk(KERN_ERR
+                      "iop_load_spu: Failed to load firmware \"%s\"\n",
+                      fw_name);
+               return retval;
+       }
+       data = (u32 *) fw_entry->data;
+
+       /* acquire ownership of memory controller */
+       switch (spu_inst) {
+       case 0:
+               mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
+               REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
+               break;
+       case 1:
+               mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
+               REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
+               break;
+       }
+       timeout = IOP_TIMEOUT;
+       do {
+               REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
+               mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
+       } while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
+       if (timeout == 0) {
+               printk(KERN_ERR "Timeout waiting to acquire MC\n");
+               retval = -EBUSY;
+               goto out;
+       }
+
+       /* write to SPU memory */
+       for (i = 0; i < (fw_entry->size/4); i++) {
+               switch (spu_inst) {
+               case 0:
+                       REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
+                       break;
+               case 1:
+                       REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
+                       break;
+               }
+               REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
+               data++;
+       }
+
+       /* release ownership of memory controller */
+       (void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);
+
+ out:
+       release_firmware(fw_entry);
+       return retval;
+}
+
+int iop_fw_load_mpu(unsigned char *fw_name)
+{
+       const unsigned int start_addr = 0;
+       reg_iop_mpu_rw_ctrl mpu_ctrl;
+        const struct firmware *fw_entry;
+       u32 *data;
+       int retval, i;
+
+       /* get firmware */
+       retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
+       if (retval != 0)
+