diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 10:01:28 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 10:01:28 -0800 |
commit | 0cf975e16927fd70f34cee20d3856246c13bb4c8 (patch) | |
tree | bb955d50f28e5d98c198701798c8341d9763299a /arch/cris/arch-v32 | |
parent | 03054de1e0b90b33e9974107d84dabd2509f5898 (diff) | |
parent | bc10ac3f2fe44e65f787d6197fd5d17304bf7d83 (diff) |
Merge branch 'cris' of git://www.jni.nu/cris
* 'cris' of git://www.jni.nu/cris: (158 commits)
CRIS v32: Remove hwregs/timer_defs.h, it is now architecture specific.
CRIS v32: Change drivers/i2c.c locking.
CRIS v32: Rewrite ARTPEC-3 gpio driver to avoid volatiles and general cleanup.
CRIS: Add new timerfd syscall entries.
MAINTAINERS: Add my information for the CRIS port.
CRIS v32: Correct spelling of bandwidth in function name.
CRIS v32: Clean up nandflash.c for ARTPEC-3 and ETRAX FS.
CRIS v10: Cleanup of drivers/gpio.c
CRIS v10: drivers/net/cris/eth_v10.c rename LED defines to CRIS_LED to avoid name clash.
CRIS: Make io_pwm_set_period members unsigned in etraxgpio.h
CRIS: Move ETRAX_AXISFLASHMAP to common Kconfig file.
CRIS: Drop regs parameter from call to profile_tick in kernel/time.c
CRIS v32: Fix minor formatting issue in mach-a3/io.c
CRIS v32: Initialize GIO even if we're rambooting in kernel/head.S
CRIS v32: Remove kernel/arbiter.c, it now exists in machine dependent directory.
CRIS v32: Minor changes to avoid errors in asm-cris/arch-v32/hwregs/reg_rdwr.h
CRIS v32: arch-v32/hwregs/intr_vect_defs.h moved to machine dependent directory.
CRIS v32: Correct offset for TASK_pid in asm-cris/arch-v32/offset.h
CRIS v32: Move register map header to machine dependent directory.
CRIS v32: Let compiler know that memory is clobbered after a break op.
...
Diffstat (limited to 'arch/cris/arch-v32')
75 files changed, 8348 insertions, 3391 deletions
diff --git a/arch/cris/arch-v32/Kconfig b/arch/cris/arch-v32/Kconfig index d8acaa920e1..005ed2b3f7f 100644 --- a/arch/cris/arch-v32/Kconfig +++ b/arch/cris/arch-v32/Kconfig @@ -1,27 +1,73 @@ if ETRAX_ARCH_V32 +source arch/cris/arch-v32/mach-fs/Kconfig +source arch/cris/arch-v32/mach-a3/Kconfig + +source drivers/cpufreq/Kconfig + config ETRAX_DRAM_VIRTUAL_BASE hex depends on ETRAX_ARCH_V32 default "c0000000" -config ETRAX_LED1G - string "First green LED bit" +choice + prompt "Nbr of Ethernet LED groups" depends on ETRAX_ARCH_V32 + default ETRAX_NBR_LED_GRP_ONE + help + Select how many Ethernet LED groups that can be used. Usually one per Ethernet + interface is a good choice. + +config ETRAX_NBR_LED_GRP_ZERO + bool "Use zero LED groups" + help + Select this if you do not want any Ethernet LEDs. + +config ETRAX_NBR_LED_GRP_ONE + bool "Use one LED group" + help + Select this if you want one Ethernet LED group. This LED group + can be used for one or more Ethernet interfaces. However, it is + recomended that each Ethernet interface use a dedicated LED group. + +config ETRAX_NBR_LED_GRP_TWO + bool "Use two LED groups" + help + Select this if you want two Ethernet LED groups. This is the + best choice if you have more than one Ethernet interface and + would like to have separate LEDs for the interfaces. + +endchoice + +config ETRAX_LED_G_NET0 + string "Ethernet LED group 0 green LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA3" help - Bit to use for the first green LED (network LED). - Most Axis products use bit A3 here. + Bit to use for the green LED in Ethernet LED group 0. -config ETRAX_LED1R - string "First red LED bit" - depends on ETRAX_ARCH_V32 +config ETRAX_LED_R_NET0 + string "Ethernet LED group 0 red LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA4" help - Bit to use for the first red LED (network LED). - Most Axis products use bit A4 here. + Bit to use for the red LED in Ethernet LED group 0. -config ETRAX_LED2G +config ETRAX_LED_G_NET1 + string "Ethernet group 1 green LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the green LED in Ethernet LED group 1. + +config ETRAX_LED_R_NET1 + string "Ethernet group 1 red LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the red LED in Ethernet LED group 1. + +config ETRAX_V32_LED2G string "Second green LED bit" depends on ETRAX_ARCH_V32 default "PA5" @@ -29,7 +75,7 @@ config ETRAX_LED2G Bit to use for the first green LED (status LED). Most Axis products use bit A5 here. -config ETRAX_LED2R +config ETRAX_V32_LED2R string "Second red LED bit" depends on ETRAX_ARCH_V32 default "PA6" @@ -37,7 +83,7 @@ config ETRAX_LED2R Bit to use for the first red LED (network LED). Most Axis products use bit A6 here. -config ETRAX_LED3G +config ETRAX_V32_LED3G string "Third green LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -45,7 +91,7 @@ config ETRAX_LED3G Bit to use for the first green LED (drive/power LED). Most Axis products use bit A7 here. -config ETRAX_LED3R +config ETRAX_V32_LED3R string "Third red LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -54,39 +100,6 @@ config ETRAX_LED3R 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 @@ -95,25 +108,11 @@ choice 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" +config ETRAX_KGDB_PORT4 + bool "Serial-4" + depends on ETRAX_SERIAL_PORTS = 5 help - Use serial port 3 for kernel debugging. + Use serial port 4 for kernel debugging. endchoice diff --git a/arch/cris/arch-v32/boot/Makefile b/arch/cris/arch-v32/boot/Makefile index 26f293ab961..3f91349c5f1 100644 --- a/arch/cris/arch-v32/boot/Makefile +++ b/arch/cris/arch-v32/boot/Makefile @@ -1,14 +1,21 @@ # # arch/cris/arch-v32/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary -R .note -R .comment -compressed/vmlinuz: $(objtree)/vmlinux - @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - rm -f zImage tools/build compressed/vmlinux.out - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin + +$(obj)/zImage: $(obj)/compressed/vmlinux + @cp $< $@ + @echo ' Kernel: $@ is ready' diff --git a/arch/cris/arch-v32/boot/compressed/Makefile b/arch/cris/arch-v32/boot/compressed/Makefile index 609692f9d5e..2c8c2c3039c 100644 --- a/arch/cris/arch-v32/boot/compressed/Makefile +++ b/arch/cris/arch-v32/boot/compressed/Makefile @@ -1,41 +1,30 @@ # -# lx25/arch/cris/arch-v32/boot/compressed/Makefile +# arch/cris/arch-v32/boot/compressed/Makefile # -# create a compressed vmlinux image from the original vmlinux files and romfs -# - -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +asflags-y += -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch +ccflags-y += -O2 -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/decompress.ld +obj-y = head.o misc.o +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o - -# files to compress -SYSTEM = $(objtree)/vmlinux.bin - -all: vmlinuz - -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin -$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz - rm -f piggy.img - cp $(objtree)/vmlinuz $(src) +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $@ +targets := vmlinux piggy.gz decompress.o decompress.bin -# gzip the kernel image +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -piggy.img: $(SYSTEM) - cat $(SYSTEM) | gzip -f -9 > piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -clean: - rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS) +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff --git a/arch/cris/arch-v32/boot/compressed/README b/arch/cris/arch-v32/boot/compressed/README index e33691d15c5..182c5d75784 100644 --- a/arch/cris/arch-v32/boot/compressed/README +++ b/arch/cris/arch-v32/boot/compressed/README @@ -1,6 +1,5 @@ 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. diff --git a/arch/cris/arch-v32/boot/compressed/head.S b/arch/cris/arch-v32/boot/compressed/head.S index 34cea10a899..f86208caf32 100644 --- a/arch/cris/arch-v32/boot/compressed/head.S +++ b/arch/cris/arch-v32/boot/compressed/head.S @@ -2,13 +2,12 @@ * Code that sets up the DRAM registers, calls the * decompressor to unpack the piggybacked kernel, and jumps. * - * Copyright (C) 1999 - 2003, Axis Communications AB + * Copyright (C) 1999 - 2006, Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY -#include <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> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 @@ -22,114 +21,49 @@ 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. + START_CLOCKS + + ;; Initialize the DRAM registers. cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? beq dram_init_finished nop -#include "../../lib/dram_init.S" +#include "../../mach/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] + GIO_INIT ;; 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. + ;; Figure out where the compressed piggyback image is. + ;; It is either in [NOR] flash (we don't want to copy it + ;; to DRAM before unpacking), or copied to DRAM + ;; by the [NAND] flash boot loader. + ;; The piggyback image is at _edata, but relative to where the + ;; image is actually located in memory, not where it is linked + ;; (the decompressor is linked at 0x40700000+ and runs there). ;; Use (_edata - herami) as offset to the current PC. - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - beq hereami2 - nop hereami: lapcq ., $r5 ; get PC and.d 0x7fffffff, $r5 ; strip any non-cache bit - move.d $r5, $r0 ; save for later - flash address of 'herami' + move.d $r5, $r0 ; source address of 'herami' add.d _edata, $r5 sub.d hereami, $r5 ; r5 = flash address of '_edata' move.d hereami, $r1 ; destination - ba 2f - nop -hereami2: - lapcq ., $r5 ; get PC - and.d 0x00ffffff, $r5 ; strip any non-cache bit - move.d $r5, $r6 - or.d 0x40200000, $r6 - move.d $r6, $r0 ; save for later - flash address of 'herami' - add.d _edata, $r5 - sub.d hereami2, $r5 ; r5 = flash address of '_edata' - add.d 0x40200000, $r5 - move.d hereami2, $r1 ; destination -2: + ;; Copy text+data to DRAM move.d _edata, $r2 ; end destination -1: move.w [$r0+], $r3 - move.w $r3, [$r1+] - cmp.d $r2, $r1 +1: move.w [$r0+], $r3 ; from herami+ source + move.w $r3, [$r1+] ; to hereami+ destination (linked address) + cmp.d $r2, $r1 ; finish when destination == _edata bcs 1b nop - move.d input_data, $r0 ; for the decompressor move.d $r5, [$r0] ; for the decompressor @@ -144,16 +78,24 @@ hereami2: nop ;; Save command line magic and address. - move.d _cmd_line_magic, $r12 - move.d $r10, [$r12] - move.d _cmd_line_addr, $r12 - move.d $r11, [$r12] + move.d _cmd_line_magic, $r0 + move.d $r10, [$r0] + move.d _cmd_line_addr, $r0 + move.d $r11, [$r0] + + ;; Save boot source indicator + move.d _boot_source, $r0 + move.d $r12, [$r0] ;; Do the decompression and save compressed size in _inptr jsr decompress_kernel nop + ;; Restore boot source indicator + move.d _boot_source, $r12 + move.d [$r12], $r12 + ;; Restore command line magic and address. move.d _cmd_line_magic, $r10 move.d [$r10], $r10 @@ -166,11 +108,10 @@ hereami2: 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 + cmp.d 0x40000000, $r9 ; image in DRAM ? + blo enter_kernel ; no, must be [NOR] flash, jump + nop ; delay slot + and.d 0x001fffff, $r9 ; assume compressed kernel was < 2M enter_kernel: ;; Enter the decompressed kernel @@ -186,7 +127,7 @@ _cmd_line_magic: .dword 0 _cmd_line_addr: .dword 0 -is_nand_boot: - .dword 0 +_boot_source: + .dword 0 -#include "../../lib/hw_settings.S" +#include "../../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/boot/compressed/misc.c b/arch/cris/arch-v32/boot/compressed/misc.c index 0169ba1ca9c..55b2695c5d7 100644 --- a/arch/cris/arch-v32/boot/compressed/misc.c +++ b/arch/cris/arch-v32/boot/compressed/misc.c @@ -1,8 +1,6 @@ /* * 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. * @@ -22,9 +20,13 @@ #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> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/pinmux_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* * gzip declarations @@ -85,7 +87,6 @@ static unsigned outcnt = 0; /* bytes in output buffer */ # 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 **); @@ -186,6 +187,8 @@ memset(void* s, int c, size_t n) char *ss = (char*)s; for (i=0;i<n;i++) ss[i] = c; + + return s; } void* @@ -196,6 +199,8 @@ memcpy(void* __dest, __const void* __src, char *d = (char *)__dest, *s = (char *)__src; for (i=0;i<__n;i++) d[i] = s[i]; + + return __dest; } /* =========================================================================== @@ -225,15 +230,15 @@ flush_window() static void error(char *x) { - puts("\n\n"); + puts("\r\n\n"); puts(x); - puts("\n\n -- System halted\n"); + puts("\r\n\n -- System halted\n"); while(1); /* Halt */ } void -setup_normal_output_buffer() +setup_normal_output_buffer(void) { output_data = (char *)KERNEL_LOAD_ADR; } @@ -262,15 +267,17 @@ serial_setup(reg_scope_instances regi_ser) rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div); tr_ctrl.stop_bits = 1; /* 2 stop bits. */ + tr_ctrl.en = 1; /* enable transmitter */ + rec_ctrl.en = 1; /* enabler receiver */ /* - * The baudrate setup is a bit fishy, but in the end the transceiver is - * set to 4800 and the receiver to 115200. The magic value is - * 29.493 MHz. + * The baudrate setup used to be a bit fishy, but now transmitter and + * receiver are both set to the intended baud rate, 115200. + * The magic value is 29.493 MHz. */ tr_ctrl.base_freq = regk_ser_f29_493; rec_ctrl.base_freq = regk_ser_f29_493; - tr_baud.div = (29493000 / 8) / 4800; + tr_baud.div = (29493000 / 8) / 115200; rec_baud.div = (29493000 / 8) / 115200; REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); @@ -280,25 +287,52 @@ serial_setup(reg_scope_instances regi_ser) } void -decompress_kernel() +decompress_kernel(void) { char revision; - /* input_data is set in head.S */ - inbuf = input_data; +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + reg_pinmux_rw_hwprot hwprot; + +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + reg_clkgen_rw_clk_ctrl clk_ctrl; + + /* Enable corresponding clock region when serial 1..3 selected */ + + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); +#endif + + /* pinmux setup for ports 1..3 */ + hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); +#endif #ifdef CONFIG_ETRAX_DEBUG_PORT0 serial_setup(regi_ser0); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT1 + hwprot.ser1 = regk_pinmux_yes; serial_setup(regi_ser1); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT2 + hwprot.ser2 = regk_pinmux_yes; serial_setup(regi_ser2); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT3 + hwprot.ser3 = regk_pinmux_yes; serial_setup(regi_ser3); #endif +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); +#endif + + /* input_data is set in head.S */ + inbuf = input_data; setup_normal_output_buffer(); @@ -307,11 +341,11 @@ decompress_kernel() __asm__ volatile ("move $vr,%0" : "=rm" (revision)); if (revision < 32) { - puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n"); + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n"); while(1); } - puts("Uncompressing Linux...\n"); + puts("Uncompressing Linux...\r\n"); gunzip(); - puts("Done. Now booting the kernel.\n"); + puts("Done. Now booting the kernel.\r\n"); } diff --git a/arch/cris/arch-v32/boot/rescue/Makefile b/arch/cris/arch-v32/boot/rescue/Makefile index f668a819872..c0987795dcb 100644 --- a/arch/cris/arch-v32/boot/rescue/Makefile +++ b/arch/cris/arch-v32/boot/rescue/Makefile @@ -1,36 +1,27 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 -I $(srctree)/include/asm/arch/mach/ \ + -I $(srctree)/include/asm/arch +asflags-y += -I $(srctree)/include/asm/arch/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/rescue.ld +LDPOSTFLAGS = -lgcc OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-$(CONFIG_ETRAX_AXISFLASHMAP) = head.o +OBJECT := $(obj)/head.o -all: $(target)/rescue.bin +targets := rescue.o rescue.bin -rescue: rescue.bin - # do nothing +quiet_cmd_ldlibgcc = LD $@ +cmd_ldlibgcc = $(LD) $(LDFLAGS) $(filter-out FORCE,$^) $(LDPOSTFLAGS) -o $@ -$(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) +$(obj)/rescue.o: $(OBJECTS) FORCE + $(call if_changed,ldlibgcc) -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) diff --git a/arch/cris/arch-v32/boot/rescue/head.S b/arch/cris/arch-v32/boot/rescue/head.S index 8cdb4011bc1..5f846b7700a 100644 --- a/arch/cris/arch-v32/boot/rescue/head.S +++ b/arch/cris/arch-v32/boot/rescue/head.S @@ -1,38 +1,26 @@ -/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $ +/* + * Just get started by jumping to CONFIG_ETRAX_PTABLE_SECTOR to start + * kernel decompressor. + * + * In practice, this only works for NOR flash (or some convoluted RAM boot) + * and hence is not really useful for Artpec-3, so it's Etrax FS / NOR only. * - * 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 <asm/arch/hwregs/reg_map_asm.h> -#include <asm/arch/hwregs/config_defs_asm.h> +#include <mach/startup.inc> - .text +#ifdef CONFIG_ETRAX_AXISFLASHMAP - ;; 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] +;; Code - ;; 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 + .text +start: -#include "../../lib/nand_init.S" + ;; Start clocks for used blocks. + START_CLOCKS - ;; No NAND found move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser + jump $r10 ; Jump to decompressor nop -copy_complete: - move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser - nop +#endif diff --git a/arch/cris/arch-v32/boot/rescue/rescue.ld b/arch/cris/arch-v32/boot/rescue/rescue.ld index 42b11aa122b..8ac646bc1a2 100644 --- a/arch/cris/arch-v32/boot/rescue/rescue.ld +++ b/arch/cris/arch-v32/boot/rescue/rescue.ld @@ -1,20 +1,43 @@ +/*#OUTPUT_FORMAT(elf32-us-cris) */ +OUTPUT_ARCH (crisv32) +/* Now that NAND support has been stripped, this file could be simplified, + * but it doesn't do any harm on the other hand so why bother. */ + MEMORY { - flash : ORIGIN = 0x00000000, - LENGTH = 0x00100000 + bootblk : ORIGIN = 0x38000000, + LENGTH = 0x00004000 + intmem : ORIGIN = 0x38004000, + LENGTH = 0x00005000 } SECTIONS { .text : { - stext = . ; + _stext = . ; *(.text) - etext = . ; - } > flash + *(.init.text) + *(.rodata) + *(.rodata.*) + _etext = . ; + } > bootblk .data : { *(.data) - edata = . ; - } > flash + _edata = . ; + } > bootblk + .bss : + { + _bss = . ; + *(.bss) + _end = ALIGN( 0x10 ) ; + } > intmem + + /* Get rid of stuff from EXPORT_SYMBOL(foo). */ + /DISCARD/ : + { + *(__ksymtab_strings) + *(__ksymtab) + } } diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index c329cce2a0c..2a92cb1886c 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -4,64 +4,102 @@ config ETRAX_ETHERNET bool "Ethernet support" depends on ETRAX_ARCH_V32 select NET_ETHERNET + select MII 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" +config ETRAX_NO_PHY + bool "PHY not present" depends on ETRAX_ETHERNET - depends on ETRAX_STREAMCOPROC - default y + default N help - Hardware acceleration of checksumming and scatter/gather + This option disables all MDIO communication with an ethernet + transceiver connected to the MII interface. This option shall + typically be enabled if the MII interface is connected to a + switch. This option should normally be disabled. If enabled, + speed and duplex will be locked to 100 Mbit and full duplex. config ETRAX_ETHERNET_IFACE0 depends on ETRAX_ETHERNET bool "Enable network interface 0" config ETRAX_ETHERNET_IFACE1 - depends on ETRAX_ETHERNET + depends on (ETRAX_ETHERNET && ETRAXFS) bool "Enable network interface 1 (uses DMA6 and DMA7)" +config ETRAX_ETHERNET_GBIT + depends on (ETRAX_ETHERNET && CRIS_MACH_ARTPEC3) + bool "Enable gigabit Ethernet support" + choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + prompt "Eth0 led group" + depends on ETRAX_ETHERNET_IFACE0 + default ETRAX_ETH0_USE_LEDGRP0 -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" +config ETRAX_ETH0_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use LED grp 0 for eth0 - 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_ETH0_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth0 -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" +config ETRAX_ETH0_USE_LEDGRPNULL + bool "Use no LEDs for eth0" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use no LEDs for eth0 +endchoice - Selecting LED_on_when_activity will light the LED only when - there is activity. +choice + prompt "Eth1 led group" + depends on ETRAX_ETHERNET_IFACE1 + default ETRAX_ETH1_USE_LEDGRP1 + +config ETRAX_ETH1_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 0 for eth1 - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. +config ETRAX_ETH1_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth1 +config ETRAX_ETH1_USE_LEDGRPNULL + bool "Use no LEDs for eth1" + help + Use no LEDs for eth1 endchoice config ETRAXFS_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V32 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE help Enables the ETRAX FS serial driver for ser0 (ttyS0) You probably want this enabled. +config ETRAX_RS485 + bool "RS-485 support" + depends on ETRAXFS_SERIAL + help + Enables support for RS-485 serial communication. + +config ETRAX_RS485_DISABLE_RECEIVER + bool "Disable serial receiver" + depends on ETRAX_RS485 + help + It is necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + config ETRAX_SERIAL_PORT0 bool "Serial port 0 enabled" depends on ETRAXFS_SERIAL @@ -72,50 +110,28 @@ config ETRAX_SERIAL_PORT0 ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. choice - prompt "Ser0 DMA in channel " + prompt "Ser0 default port type " depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_NO_DMA_IN + default ETRAX_SERIAL_PORT0_TYPE_232 help - What DMA channel to use for ser0. - + Type of serial port. -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "Ser0 uses no DMA for input" +config ETRAX_SERIAL_PORT0_TYPE_232 + bool "Ser0 is a RS-232 port" help - Do not use DMA for ser0 input. + Configure serial port 0 to be a RS-232 port. -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 receiving 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" +config ETRAX_SERIAL_PORT0_TYPE_485HD + bool "Ser0 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser0 output. + Configure serial port 0 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "Ser0 uses DMA6 for output" - depends on ETRAX_SERIAL_PORT0 +config ETRAX_SERIAL_PORT0_TYPE_485FD + bool "Ser0 is a full duplex RS-485 port" + depends on ETRAX_RS485 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. - + Configure serial port 0 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER0_DTR_BIT @@ -141,52 +157,28 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX FS serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA in channel " + prompt "Ser1 default port type" 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" + default ETRAX_SERIAL_PORT1_TYPE_232 help - Do not use DMA for ser1 input. + Type of serial port. -config ETRAX_SERIAL_PORT1_DMA5_IN - bool "Ser1 uses DMA5 for input" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_232 + bool "Ser1 is a RS-232 port" help - Enables the DMA5 input channel for ser1 (ttyS1). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want this on, unless you use the DMA channel for - something else. - -endchoice + Configure serial port 1 to be a RS-232 port. -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" +config ETRAX_SERIAL_PORT1_TYPE_485HD + bool "Ser1 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser1 output. + Configure serial port 1 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT1_DMA4_OUT - bool "Ser1 uses DMA4 for output" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_485FD + bool "Ser1 is a full duplex RS-485 port" + depends on ETRAX_RS485 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. - + Configure serial port 1 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER1_DTR_BIT @@ -212,52 +204,31 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX FS serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA in channel " + prompt "Ser2 default port type" depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_NO_DMA_IN + default ETRAX_SERIAL_PORT2_TYPE_232 help - What DMA channel to use for ser2. + What DMA channel to use for ser2 - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "Ser2 uses no DMA for input" +config ETRAX_SERIAL_PORT2_TYPE_232 + bool "Ser2 is a RS-232 port" help - Do not use DMA for ser2 input. + Configure serial port 2 to be a RS-232 port. -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 receiving 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" +config ETRAX_SERIAL_PORT2_TYPE_485HD + bool "Ser2 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser2 output. + Configure serial port 2 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "Ser2 uses DMA2 for output" - depends on ETRAX_SERIAL_PORT2 +config ETRAX_SERIAL_PORT2_TYPE_485FD + bool "Ser2 is a full duplex RS-485 port" + depends on ETRAX_RS485 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. - + Configure serial port 2 to be a full duplex (four wires) RS-485 port. endchoice + config ETRAX_SER2_DTR_BIT string "Ser 2 DTR bit (empty = not used)" depends on ETRAX_SERIAL_PORT2 @@ -281,71 +252,121 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX FS serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA in channel " + prompt "Ser3 default port type" depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_IN + default ETRAX_SERIAL_PORT3_TYPE_232 help What DMA channel to use for ser3. +config ETRAX_SERIAL_PORT3_TYPE_232 + bool "Ser3 is a RS-232 port" + help + Configure serial port 3 to be a RS-232 port. -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "Ser3 uses no DMA for input" +config ETRAX_SERIAL_PORT3_TYPE_485HD + bool "Ser3 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser3 input. + Configure serial port 3 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT3_DMA9_IN - bool "Ser3 uses DMA9 for input" +config ETRAX_SERIAL_PORT3_TYPE_485FD + bool "Ser3 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 3 to be a full duplex (four wires) RS-485 port. +endchoice + +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_SERIAL_PORT4 + bool "Serial port 4 enabled" + depends on ETRAXFS_SERIAL && CRIS_MACH_ARTPEC3 help - Enables the DMA9 input channel for ser3 (ttyS3). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. + Enables the ETRAX FS serial driver for ser4 (ttyS4). + +choice + prompt "Ser4 default port type" + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_TYPE_232 + help + What DMA channel to use for ser4. +config ETRAX_SERIAL_PORT4_TYPE_232 + bool "Ser4 is a RS-232 port" + help + Configure serial port 4 to be a RS-232 port. + +config ETRAX_SERIAL_PORT4_TYPE_485HD + bool "Ser4 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT4_TYPE_485FD + bool "Ser4 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a full duplex (four wires) RS-485 port. endchoice choice - prompt "Ser3 DMA out channel" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_OUT + prompt "Ser4 DMA in channel " + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_NO_DMA_IN + help + What DMA channel to use for ser4. + -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "Ser3 uses no DMA for output" +config ETRAX_SERIAL_PORT4_NO_DMA_IN + bool "Ser4 uses no DMA for input" help - Do not use DMA for ser3 output. + Do not use DMA for ser4 input. -config ETRAX_SERIAL_PORT3_DMA8_OUT - bool "Ser3 uses DMA8 for output" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SERIAL_PORT4_DMA9_IN + bool "Ser4 uses DMA9 for input" + depends on ETRAX_SERIAL_PORT4 help - Enables the DMA8 output channel for ser3 (ttyS3). + Enables the DMA9 input channel for ser4 (ttyS4). If you do not enable DMA, an interrupt for each character will be - used when transmitting data. + used when receiveing 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_SER4_DTR_BIT + string "Ser 4 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_RI_BIT - string "Ser 3 RI bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_RI_BIT + string "Ser 4 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_DSR_BIT - string "Ser 3 DSR bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_DSR_BIT + string "Ser 4 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_SER3_CD_BIT - string "Ser 3 CD bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 + string "Ser 4 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_RS485 bool "RS-485 support" - depends on ETRAX_SERIAL + depends on ETRAXFS_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>. @@ -356,22 +377,6 @@ config ETRAX_RS485_DISABLE_RECEIVER 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_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" @@ -394,7 +399,7 @@ config ETRAX_SYNCHRONOUS_SERIAL0_DMA config ETRAX_SYNCHRONOUS_SERIAL_PORT1 bool "Synchronous serial port 1 enabled" - depends on ETRAX_SYNCHRONOUS_SERIAL + depends on ETRAX_SYNCHRONOUS_SERIAL && ETRAXFS help Enabled synchronous serial port 1. @@ -405,6 +410,31 @@ config ETRAX_SYNCHRONOUS_SERIAL1_DMA A synchronous serial port can run in manual or DMA mode. Selecting this option will make it run in DMA mode. +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V32 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_AXISFLASHMAP_MTD0WHOLE + bool "MTD0 is whole boot flash device" + depends on ETRAX_AXISFLASHMAP + default N + help + When this option is not set, mtd0 refers to the first partition + on the boot flash device. When set, mtd0 refers to the whole + device, with mtd1 referring to the first partition etc. + config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -425,42 +455,32 @@ config ETRAX_NANDFLASH This option enables MTD mapping of NAND flash devices. Needed to use NAND flash memories. If unsure, say Y. +config ETRAX_NANDBOOT + bool "Boot from NAND flash" + depends on ETRAX_NANDFLASH + help + This options enables booting from NAND flash devices. + Say Y if your boot code, kernel and root file system is in + NAND flash. Say N if they are in NOR flash. + config ETRAX_I2C bool "I2C driver" depends on ETRAX_ARCH_V32 help - This option enabled the I2C driver used by e.g. the RTC driver. + This option enables the I2C driver used by e.g. the RTC driver. -config ETRAX_I2C_DATA_PORT +config ETRAX_V32_I2C_DATA_PORT string "I2C data pin" depends on ETRAX_I2C help The pin to use for I2C data. -config ETRAX_I2C_CLK_PORT +config ETRAX_V32_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 @@ -476,33 +496,36 @@ config ETRAX_GPIO Remember that you need to setup the port directions appropriately in the General configuration. -config ETRAX_PA_BUTTON_BITMASK - hex "PA-buttons bitmask" +config ETRAX_VIRTUAL_GPIO + bool "Virtual GPIO support" depends on ETRAX_GPIO - default "0x02" help - This is a bitmask (8 bits) with information about what bits on PA - that are used for buttons. - Most products has a so called TEST button on PA1, if that is true - use 0x02 here. - Use 00 if there are no buttons on PA. - If the bitmask is <> 00 a button driver will be included in the gpio - driver. ETRAX general I/O support must be enabled. + Enables the virtual Etrax general port device (major 120, minor 6). + It uses an I/O expander for the I2C-bus. + +config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN + int "Virtual GPIO interrupt pin on PA pin" + range 0 7 + depends on ETRAX_VIRTUAL_GPIO + help + The pin to use on PA for virtual gpio interrupt. config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS 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. + You probably want 0 here, but it depends on your hardware. config ETRAX_PA_CHANGEABLE_BITS hex "PA user changeable bits mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS 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. @@ -511,17 +534,19 @@ config ETRAX_PA_CHANGEABLE_BITS config ETRAX_PB_CHANGEABLE_DIR hex "PB user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS 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. + You probably want 0 here, but it depends on your hardware. config ETRAX_PB_CHANGEABLE_BITS hex "PB user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS 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. @@ -530,17 +555,19 @@ config ETRAX_PB_CHANGEABLE_BITS config ETRAX_PC_CHANGEABLE_DIR hex "PC user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS 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. + You probably want 0 here, but it depends on your hardware. config ETRAX_PC_CHANGEABLE_BITS hex "PC user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if ETRAXFS 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. @@ -548,7 +575,7 @@ config ETRAX_PC_CHANGEABLE_BITS config ETRAX_PD_CHANGEABLE_DIR hex "PD user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -558,7 +585,7 @@ config ETRAX_PD_CHANGEABLE_DIR config ETRAX_PD_CHANGEABLE_BITS hex "PD user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -567,7 +594,7 @@ config ETRAX_PD_CHANGEABLE_BITS config ETRAX_PE_CHANGEABLE_DIR hex "PE user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PE @@ -577,20 +604,36 @@ config ETRAX_PE_CHANGEABLE_DIR config ETRAX_PE_CHANGEABLE_BITS hex "PE user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS 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_PV_CHANGEABLE_DIR + hex "PV user changeable dir mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x0000 here, but it depends on your hardware. + +config ETRAX_PV_CHANGEABLE_BITS + hex "PV user changeable bits mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change the value on using ioctl's. + Bit set = changeable. + config ETRAX_CARDBUS bool "Cardbus support" depends on ETRAX_ARCH_V32 - select PCCARD - select CARDBUS select HOTPLUG - select PCCARD_NONSTATIC help Enabled the ETRAX Cardbus driver. @@ -613,4 +656,202 @@ config ETRAX_STREAMCOPROC This option enables a driver for the stream co-processor for cryptographic operations. +source drivers/mmc/Kconfig + +config ETRAX_MMC_IOP + tristate "MMC/SD host driver using IO-processor" + depends on ETRAX_ARCH_V32 && MMC + help + This option enables the SD/MMC host controller interface. + The host controller is implemented using the built in + IO-Processor. Only the SPU is used in this implementation. + +config ETRAX_SPI_MMC +# Make this one of several "choices" (possible simultaneously but +# suggested uniquely) when an IOP driver emerges for "real" MMC/SD +# protocol support. + tristate + depends on !ETRAX_MMC_IOP + default MMC + select SPI + select MMC_SPI + select ETRAX_SPI_MMC_BOARD + +# For the parts that can't be a module (due to restrictions in +# framework elsewhere). +config ETRAX_SPI_MMC_BOARD + boolean + default n + +# While the board info is MMC_SPI only, the drivers are written to be +# independent of MMC_SPI, so we'll keep SPI non-dependent on the +# MMC_SPI config choices (well, except for a single depends-on-line +# for the board-info file until a separate non-MMC SPI board file +# emerges). +# FIXME: When that happens, we'll need to be able to ask for and +# configure non-MMC SPI ports together with MMC_SPI ports (if multiple +# SPI ports are enabled). + +config SPI_ETRAX_SSER + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using an synchronous serial (sser) port as a + SPI master controller on Axis ETRAX FS and later. The + driver can be configured to use any sser port. + +config SPI_ETRAX_GPIO + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using GPIO pins port as a SPI master controller + on Axis ETRAX FS and later. The driver can be configured to + use any GPIO pins. + +config ETRAX_SPI_SSER0 + tristate "SPI using synchronous serial port 0 (sser0)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m + default y if MMC_SPI=y + default y if MMC_SPI=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 0, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER0_DMA + bool "DMA for SPI on sser0 enabled" + depends on ETRAX_SPI_SSER0 + depends on !ETRAX_SERIAL_PORT1_DMA4_OUT && !ETRAX_SERIAL_PORT1_DMA5_IN + default y + help + Say Y if using DMA (dma4/dma5) for SPI on synchronous serial port 0. + +config ETRAX_SPI_MMC_CD_SSER0_PIN + string "MMC/SD card detect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER0_PIN + string "MMC/SD card write-protect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_SSER1 + tristate "SPI using synchronous serial port 1 (sser1)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 1, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER1_DMA + bool "DMA for SPI on sser1 enabled" + depends on ETRAX_SPI_SSER1 && !ETRAX_ETHERNET_IFACE1 + depends on !ETRAX_SERIAL_PORT0_DMA6_OUT && !ETRAX_SERIAL_PORT0_DMA7_IN + default y + help + Say Y if using DMA (dma6/dma7) for SPI on synchronous serial port 1. + +config ETRAX_SPI_MMC_CD_SSER1_PIN + string "MMC/SD card detect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd12" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER1_PIN + string "MMC/SD card write-protect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd9" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_GPIO + tristate "Bitbanged SPI using gpio pins" + depends on ETRAX_SPI_MMC + select SPI_ETRAX_GPIO + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + help + Say Y for an MMC/SD socket connected to general I/O pins (but not + a complete synchronous serial ports), or for devices using the SPI + protocol on general I/O pins. Slow and slows down the system. + Say m to build it as a module, which will be called spi_crisv32_gpio. + (You need to select MMC separately.) + +# The default match that of sser0, only because that's how it was tested. +config ETRAX_SPI_CS_PIN + string "SPI chip select pin" + depends on ETRAX_SPI_GPIO + default "pc3" + help + The pin to use for SPI chip select. + +config ETRAX_SPI_CLK_PIN + string "SPI clock pin" + depends on ETRAX_SPI_GPIO + default "pc1" + help + The pin to use for the SPI clock. + +config ETRAX_SPI_DATAIN_PIN + string "SPI MISO (data in) pin" + depends on ETRAX_SPI_GPIO + default "pc16" + help + The pin to use for SPI data in from the device. + +config ETRAX_SPI_DATAOUT_PIN + string "SPI MOSI (data out) pin" + depends on ETRAX_SPI_GPIO + default "pc0" + help + The pin to use for SPI data out to the device. + +config ETRAX_SPI_MMC_CD_GPIO_PIN + string "MMC/SD card detect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_GPIO_PIN + string "MMC/SD card write-protect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +# Avoid choices causing non-working configs by conditionalizing the inclusion. +if ETRAX_SPI_MMC +source drivers/spi/Kconfig +endif + endif diff --git a/arch/cris/arch-v32/drivers/Makefile b/arch/cris/arch-v32/drivers/Makefile index a359cd20ae7..e8c02437eda 100644 --- a/arch/cris/arch-v32/drivers/Makefile +++ b/arch/cris/arch-v32/drivers/Makefile @@ -4,10 +4,11 @@ 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_ETRAXFS) += mach-fs/ +obj-$(CONFIG_CRIS_MACH_ARTPEC3) += mach-a3/ 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/ +obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o diff --git a/arch/cris/arch-v32/drivers/axisflashmap.c b/arch/cris/arch-v32/drivers/axisflashmap.c index c5ff95e1826..51e1e85df96 100644 --- a/arch/cris/arch-v32/drivers/axisflashmap.c +++ b/arch/cris/arch-v32/drivers/axisflashmap.c @@ -1,7 +1,7 @@ /* * Physical mapping layer for MTD using the Axis partitiontable format * - * Copyright (c) 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 2001-2007 Axis Communications AB * * This file is under the GPL. * @@ -10,9 +10,6 @@ * 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> @@ -27,7 +24,8 @@ #include <linux/mtd/mtdram.h> #include <linux/mtd/partitions.h> -#include <asm/arch/hwregs/config_defs.h> +#include <linux/cramfs_fs.h> + #include <asm/axisflashmap.h> #include <asm/mmu.h> @@ -37,16 +35,24 @@ #define FLASH_UNCACHED_ADDR KSEG_E #define FLASH_CACHED_ADDR KSEG_F +#define PAGESIZE (512) + #if CONFIG_ETRAX_FLASH_BUSWIDTH==1 #define flash_data __u8 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 #define flash_data __u16 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 -#define flash_data __u16 +#define flash_data __u32 #endif /* From head.S */ -extern unsigned long romfs_start, romfs_length, romfs_in_flash; +extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */ +extern unsigned long romfs_start, romfs_length; +extern unsigned long nand_boot; /* 1 when booted from nand flash */ + +struct partition_name { + char name[6]; +}; /* The master mtd for the entire flash. */ struct mtd_info* axisflash_mtd = NULL; @@ -112,32 +118,20 @@ static struct map_info map_cse1 = { .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE }; -/* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 +#define MAX_PARTITIONS 7 +#ifdef CONFIG_ETRAX_NANDBOOT +#define NUM_DEFAULT_PARTITIONS 4 +#define DEFAULT_ROOTFS_PARTITION_NO 2 +#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */ +#else +#define NUM_DEFAULT_PARTITIONS 3 +#define DEFAULT_ROOTFS_PARTITION_NO (-1) +#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */ +#endif -/* - * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the - * size of one flash block and "filesystem"-partition needs 5 blocks to be able - * to use JFFS. - */ -static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { - { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0 - }, - { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR - }, - { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) - } -}; +#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS) +#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS +#endif /* Initialize the ones normally used. */ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { @@ -178,6 +172,56 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; + +/* If no partition-table was found, we use this default-set. + * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most + * likely the size of one flash block and "filesystem"-partition needs + * to be >=5 blocks to be able to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, +#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR) +#ifdef CONFIG_ETRAX_NANDBOOT + { + .name = "rootfs", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = FILESYSTEM_SECTOR + }, +#undef FILESYSTEM_SECTOR +#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR) +#endif + { + .name = "rwfs", + .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR, + .offset = FILESYSTEM_SECTOR + } +}; + +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + +/* Auxilliary partition if we find another flash */ +static struct mtd_partition aux_partition = { + .name = "aux", + .size = 0, + .offset = 0 +}; + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -191,7 +235,7 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) map_cs->name, map_cs->size, map_cs->map_priv_1); #ifdef CONFIG_MTD_CFI - mtd_cs = do_map_probe("cfi_probe", map_cs); + mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE if (!mtd_cs) @@ -204,7 +248,7 @@ static struct mtd_info *probe_cs(struct map_info *map_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 boundaries. + * 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 @@ -216,9 +260,8 @@ 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]; + struct mtd_info *mtds[2]; int count = 0; if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL) @@ -226,12 +269,7 @@ static struct mtd_info *flash_probe(void) if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL) mtds[count++] = mtd_cse1; -#ifdef CONFIG_ETRAX_NANDFLASH - if ((mtd_nand = crisv32_nand_flash_probe()) != NULL) - mtds[count++] = mtd_nand; -#endif - - if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) { + if (!mtd_cse0 && !mtd_cse1) { /* No chip found. */ return NULL; } @@ -245,9 +283,7 @@ static struct mtd_info *flash_probe(void) * So we use the MTD concatenation layer instead of further * complicating the probing procedure. */ - mtd_total = mtd_concat_create(mtds, - count, - "cse0+cse1+nand"); + mtd_total = mtd_concat_create(mtds, count, "cse0+cse1"); #else printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " "(mis)configuration!\n", map_cse0.name, map_cse1.name); @@ -255,61 +291,162 @@ static struct mtd_info *flash_probe(void) #endif if (!mtd_total) { printk(KERN_ERR "%s and %s: Concatenation failed!\n", - map_cse0.name, map_cse1.name); + map_cse0.name, map_cse1.name); /* The best we can do now is to only use what we found - * at cse0. - */ + * at cse0. */ mtd_total = mtd_cse0; map_destroy(mtd_cse1); } - } else { - mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand; - - } + } else + mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1; return mtd_total; } -extern unsigned long crisv32_nand_boot; -extern unsigned long crisv32_nand_cramfs_offset; - /* * Probe the flash chip(s) and, if it succeeds, read the partition-table * and register the partitions with MTD. */ static int __init init_axis_flash(void) { - struct mtd_info *mymtd; + struct mtd_info *main_mtd; + struct mtd_info *aux_mtd = NULL; int err = 0; int pidx = 0; struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; - int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n"; - static char page[512]; + int ptable_ok = 0; + static char page[PAGESIZE]; size_t len; + int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */ + int part; + + /* We need a root fs. If it resides in RAM, we need to use an + * MTDRAM device, so it must be enabled in the kernel config, + * but its size must be configured as 0 so as not to conflict + * with our usage. + */ +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + if (!romfs_in_flash && !nand_boot) { + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device; configure CONFIG_MTD_MTDRAM with size = 0!\n"); + panic("This kernel cannot boot from RAM!\n"); + } +#endif + +#ifndef CONFIG_ETRAX_VCS_SIM + main_mtd = flash_probe(); + if (main_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NOR flash memory.\n", + main_mtd->name, main_mtd->size); + +#ifdef CONFIG_ETRAX_NANDFLASH + aux_mtd = crisv32_nand_flash_probe(); + if (aux_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n", + aux_mtd->name, aux_mtd->size); + +#ifdef CONFIG_ETRAX_NANDBOOT + { + struct mtd_info *tmp_mtd; -#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); + printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, " + "making NAND flash primary device.\n"); + tmp_mtd = main_mtd; + main_mtd = aux_mtd; + aux_mtd = tmp_mtd; + } +#endif /* CONFIG_ETRAX_NANDBOOT */ +#endif /* CONFIG_ETRAX_NANDFLASH */ - if (!mymtd) { + if (!main_mtd && !aux_mtd) { /* There's no reason to use this module if no flash chip can * be identified. Make sure that's understood. */ printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); - } else { - printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", - mymtd->name, mymtd->size); - axisflash_mtd = mymtd; } - if (mymtd) { - mymtd->owner = THIS_MODULE; +#if 0 /* Dump flash memory so we can see what is going on */ + if (main_mtd) { + int sectoraddr, i; + for (sectoraddr = 0; sectoraddr < 2*65536+4096; + sectoraddr += PAGESIZE) { + main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len, + page); + printk(KERN_INFO + "Sector at %d (length %d):\n", + sectoraddr, len); + for (i = 0; i < PAGESIZE; i += 16) { + printk(KERN_INFO + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + page[i] & 255, page[i+1] & 255, + page[i+2] & 255, page[i+3] & 255, + page[i+4] & 255, page[i+5] & 255, + page[i+6] & 255, page[i+7] & 255, + page[i+8] & 255, page[i+9] & 255, + page[i+10] & 255, page[i+11] & 255, + page[i+12] & 255, page[i+13] & 255, + page[i+14] & 255, page[i+15] & 255); + } + } + } +#endif + + if (main_mtd) { + main_mtd->owner = THIS_MODULE; + axisflash_mtd = main_mtd; + + loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR; + + /* First partition (rescue) is always set to the default. */ + pidx++; +#ifdef CONFIG_ETRAX_NANDBOOT + /* We know where the partition table should be located, + * it will be in first good block after that. + */ + int blockstat; + do { + blockstat = main_mtd->block_isbad(main_mtd, + ptable_sector); + if (blockstat < 0) + ptable_sector = 0; /* read error */ + else if (blockstat) + ptable_sector += main_mtd->erasesize; + } while (blockstat && ptable_sector); +#endif + if (ptable_sector) { + main_mtd->read(main_mtd, ptable_sector, PAGESIZE, + &len, page); + ptable_head = &((struct partitiontable *) page)->head; + } + +#if 0 /* Dump partition table so we can see what is going on */ + printk(KERN_INFO + "axisflashmap: flash read %d bytes at 0x%08x, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + len, CONFIG_ETRAX_PTABLE_SECTOR, + page[0] & 255, page[1] & 255, + page[2] & 255, page[3] & 255, + page[4] & 255, page[5] & 255, + page[6] & 255, page[7] & 255); + printk(KERN_INFO + "axisflashmap: partition table offset %d, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + PARTITION_TABLE_OFFSET, + page[PARTITION_TABLE_OFFSET+0] & 255, + page[PARTITION_TABLE_OFFSET+1] & 255, + page[PARTITION_TABLE_OFFSET+2] & 255, + page[PARTITION_TABLE_OFFSET+3] & 255, + page[PARTITION_TABLE_OFFSET+4] & 255, + page[PARTITION_TABLE_OFFSET+5] & 255, + page[PARTITION_TABLE_OFFSET+6] & 255, + page[PARTITION_TABLE_OFFSET+7] & 255); +#endif } - pidx++; /* First partition is always set to the default. */ if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) && (ptable_head->size < @@ -322,7 +459,6 @@ static int __init init_axis_flash(void) /* 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) + @@ -346,104 +482,170 @@ static int __init init_axis_flash(void) ptable_ok = (csum == ptable_head->checksum); /* Read the entries and use/show the info. */ - printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + printk(KERN_INFO "axisflashmap: " + "Found a%s partition table at 0x%p-0x%p.\n", (ptable_ok ? " valid" : "n invalid"), ptable_head, max_addr); /* We have found a working bootblock. Now read the - * partition table. Scan the table. It ends when - * there is 0xffffffff, that is, empty flash. + * partition table. Scan the table. It ends with 0xffffffff. */ while (ptable_ok - && ptable->offset != 0xffffffff + && ptable->offset != PARTITIONTABLE_END_MARKER && ptable < max_addr - && pidx < MAX_PARTITIONS) { + && pidx < MAX_PARTITIONS - 1) { - axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0); - axis_partitions[pidx].size = ptable->size; - - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + axis_partitions[pidx].offset = offset + ptable->offset; +#ifdef CONFIG_ETRAX_NANDFLASH + if (main_mtd->type == MTD_NANDFLASH) { + axis_partitions[pidx].size = + (((ptable+1)->offset == + PARTITIONTABLE_END_MARKER) ? + main_mtd->size : + ((ptable+1)->offset + offset)) - + (ptable->offset + offset); + + } else +#endif /* CONFIG_ETRAX_NANDFLASH */ + axis_partitions[pidx].size = ptable->size; +#ifdef CONFIG_ETRAX_NANDBOOT + /* Save partition number of jffs2 ro partition. + * Needed if RAM booting or root file system in RAM. + */ + if (!nand_boot && + ram_rootfs_partition < 0 && /* not already set */ + ptable->type == PARTITION_TYPE_JFFS2 && + (ptable->flags & PARTITION_FLAGS_READONLY_MASK) == + PARTITION_FLAGS_READONLY) + ram_rootfs_partition = pidx; +#endif /* CONFIG_ETRAX_NANDBOOT */ pidx++; ptable++; } - use_default_ptable = !ptable_ok; } - if (romfs_in_flash) { - /* Add an overlapping device for the root partition (romfs). */ + /* Decide whether to use default partition table. */ + /* Only use default table if we actually have a device (main_mtd) */ - axis_partitions[pidx].name = "romfs"; - if (crisv32_nand_boot) { - char* data = kmalloc(1024, GFP_KERNEL); - int len; - int offset = crisv32_nand_cramfs_offset & ~(1024-1); - char* tmp; - - mymtd->read(mymtd, offset, 1024, &len, data); - tmp = &data[crisv32_nand_cramfs_offset % 512]; - axis_partitions[pidx].size = *(unsigned*)(tmp + 4); - axis_partitions[pidx].offset = crisv32_nand_cramfs_offset; - kfree(data); - } else { - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; - } + struct mtd_partition *partition = &axis_partitions[0]; + if (main_mtd && !ptable_ok) { + memcpy(axis_partitions, axis_default_partitions, + sizeof(axis_default_partitions)); + pidx = NUM_DEFAULT_PARTITIONS; + ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO; + } + /* Add artificial partitions for rootfs if necessary */ + if (romfs_in_flash) { + /* rootfs is in directly accessible flash memory = NOR flash. + Add an overlapping device for the rootfs partition. */ + printk(KERN_INFO "axisflashmap: Adding partition for " + "overlapping root file system image\n"); + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].name = "romfs"; axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - - printk(KERN_INFO - " Adding readonly flash partition for romfs image:\n"); - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + ram_rootfs_partition = -1; pidx++; - } - - if (mymtd) { - if (use_default_ptable) { - printk(KERN_INFO " Using default partition table.\n"); - err = add_mtd_partitions(mymtd, axis_default_partitions, - NUM_DEFAULT_PARTITIONS); - } else { - err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } else if (romfs_length && !nand_boot) { + /* romfs exists in memory, but not in flash, so must be in RAM. + * Configure an MTDRAM partition. */ + if (ram_rootfs_partition < 0) { + /* None set yet, put it at the end */ + ram_rootfs_partition = pidx; + pidx++; } + printk(KERN_INFO "axisflashmap: Adding partition for " + "root file system image in RAM\n"); + axis_partitions[ram_rootfs_partition].size = romfs_length; + axis_partitions[ram_rootfs_partition].offset = romfs_start; + axis_partitions[ram_rootfs_partition].name = "romfs"; + axis_partitions[ram_rootfs_partition].mask_flags |= + MTD_WRITEABLE; + } - if (err) { - panic("axisflashmap could not add MTD partitions!\n"); - } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (main_mtd) { + main_partition.size = main_mtd->size; + err = add_mtd_partitions(main_mtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); } -/* CONFIG_EXTRAXFS_SIM */ #endif - if (!romfs_in_flash) { - /* Create an RAM device for the root partition (romfs). */ + /* Now, register all partitions with mtd. + * We do this one at a time so we can slip in an MTDRAM device + * in the proper place if required. */ + + for (part = 0; part < pidx; part++) { + if (part == ram_rootfs_partition) { + /* add MTDRAM partition here */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) + panic("axisflashmap: Couldn't allocate memory " + "for mtd_info!\n"); + printk(KERN_INFO "axisflashmap: Adding RAM partition " + "for rootfs image.\n"); + err = mtdram_init_device(mtd_ram, + (void *)partition[part].offset, + partition[part].size, + partition[part].name); + if (err) + panic("axisflashmap: Could not initialize " + "MTD RAM device!\n"); + /* JFFS2 likes to have an erasesize. Keep potential + * JFFS2 rootfs happy by providing one. Since image + * was most likely created for main mtd, use that + * erasesize, if available. Otherwise, make a guess. */ + mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize : + CONFIG_ETRAX_PTABLE_SECTOR); + } else { + err = add_mtd_partitions(main_mtd, &partition[part], 1); + if (err) + panic("axisflashmap: Could not add mtd " + "partition %d\n", part); + } + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ + +#ifdef CONFIG_ETRAX_VCS_SIM + /* For simulator, always use a RAM partition. + * The rootfs will be found after the kernel in RAM, + * with romfs_start and romfs_end indicating location and size. + */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap: Couldn't allocate memory for " + "mtd_info!\n"); + } -#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; + printk(KERN_INFO "axisflashmap: Adding RAM partition for romfs, " + "at %u, size %u\n", + (unsigned) romfs_start, (unsigned) romfs_length); - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { - panic("axisflashmap couldn't allocate memory for " - "mtd_info!\n"); - } + err = mtdram_init_device(mtd_ram, (void *)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap: Could not initialize MTD RAM " + "device!\n"); + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ - printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); +#ifndef CONFIG_ETRAX_VCS_SIM + if (aux_mtd) { + aux_partition.size = aux_mtd->size; + err = add_mtd_partitions(aux_mtd, &aux_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "aux mtd device!\n"); - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { - panic("axisflashmap could not initialize MTD RAM " - "device!\n"); - } -#endif } +#endif /* CONFIG_EXTRAX_VCS_SIM */ return err; } diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c index e8914d40169..9fb58202be9 100644 --- a/arch/cris/arch-v32/drivers/cryptocop.c +++ b/arch/cris/arch-v32/drivers/cryptocop.c @@ -1,8 +1,7 @@ -/* $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 + * Copyright (C) 2003-2007 Axis Communications AB */ #include <linux/init.h> @@ -25,17 +24,29 @@ #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> - - +#include <dma.h> +#include <hwregs/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect_defs.h> + +#include <hwregs/strcop.h> +#include <hwregs/strcop_defs.h> +#include <cryptocop.h> + +#ifdef CONFIG_ETRAXFS +#define IN_DMA 9 +#define OUT_DMA 8 +#define IN_DMA_INST regi_dma9 +#define OUT_DMA_INST regi_dma8 +#define DMA_IRQ DMA9_INTR_VECT +#else +#define IN_DMA 3 +#define OUT_DMA 2 +#define IN_DMA_INST regi_dma3 +#define OUT_DMA_INST regi_dma2 +#define DMA_IRQ DMA3_INTR_VECT +#endif #define DESCR_ALLOC_PAD (31) @@ -1886,14 +1897,14 @@ static void cryptocop_do_tasklet(unsigned long unused) } static irqreturn_t -dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) +dma_done_interrupt(int irq, void *dev_id) { struct cryptocop_prio_job *done_job; reg_dma_rw_ack_intr ack_intr = { .data = 1, }; - REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); DEBUG(printk("cryptocop DMA done\n")); @@ -1937,7 +1948,6 @@ dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) 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 }; @@ -1950,10 +1960,14 @@ static int init_cryptocop(void) .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"); + if (request_irq(DMA_IRQ, 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); + (void)crisv32_request_dma(OUT_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); + (void)crisv32_request_dma(IN_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); local_irq_save(flags); @@ -1963,24 +1977,19 @@ static int init_cryptocop(void) 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 */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, 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); + DMA_WR_CMD(OUT_DMA_INST, regk_dma_set_w_size4); + DMA_WR_CMD(IN_DMA_INST, regk_dma_set_w_size4); /* Enable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); /* Clear intr ack. */ - REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); local_irq_restore(flags); @@ -1991,7 +2000,6 @@ static int init_cryptocop(void) 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 }; @@ -1999,26 +2007,21 @@ static void release_cryptocop(void) 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); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); /* Disable DMAs. */ - REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ - REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */ /* Disable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); local_irq_restore(flags); - free_irq(DMA9_INTR_VECT, NULL); + free_irq(DMA_IRQ, NULL); - (void)crisv32_free_dma(8); - (void)crisv32_free_dma(9); + (void)crisv32_free_dma(OUT_DMA); + (void)crisv32_free_dma(IN_DMA); } @@ -2076,13 +2079,13 @@ static void cryptocop_job_queue_close(void) 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 = REG_RD(dma, OUT_DMA_INST, rw_cfg); dma_out_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg); + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_out_cfg); - dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg); + dma_in_cfg = REG_RD(dma, IN_DMA_INST, rw_cfg); dma_in_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg); + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_in_cfg); /* Disble the cryptocop. */ rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg); @@ -2226,10 +2229,11 @@ static void cryptocop_start_job(void) &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)); + flush_dma_context(&pj->iop->ctx_in); + DMA_START_CONTEXT(IN_DMA_INST, virt_to_phys(&pj->iop->ctx_in)); /* Start output DMA. */ - DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out)); + DMA_START_CONTEXT(OUT_DMA_INST, virt_to_phys(&pj->iop->ctx_out)); spin_unlock_irqrestore(&running_job_lock, running_job_flags); DEBUG(printk("cryptocop_start_job: exiting\n")); diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c index f1edd2e359b..c2fb7a5c139 100644 --- a/arch/cris/arch-v32/drivers/i2c.c +++ b/arch/cris/arch-v32/drivers/i2c.c @@ -19,10 +19,10 @@ *! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 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> @@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ + /****************** VARIABLE SECTION ************************************/ static struct crisv32_iopin cris_i2c_clk; @@ -252,6 +254,7 @@ i2c_getack(void) * generate ACK clock pulse */ i2c_clk(I2C_CLOCK_HIGH); +#if 0 /* * Use PORT PB instead of I2C * for input. (I2C not working) @@ -264,6 +267,8 @@ i2c_getack(void) i2c_data(1); i2c_disable(); i2c_dir_in(); +#endif + /* * now wait for ack */ @@ -271,11 +276,11 @@ i2c_getack(void) /* * check for ack */ - if(i2c_getbit()) + if (i2c_getbit()) ack = 0; i2c_delay(CLOCK_HIGH_TIME/2); - if(!ack){ - if(!i2c_getbit()) /* receiver pulled SDA low */ + if (!ack) { + if (!i2c_getbit()) /* receiver pulld SDA low */ ack = 1; i2c_delay(CLOCK_HIGH_TIME/2); } @@ -285,6 +290,7 @@ i2c_getack(void) * before we enable our output. If we keep data high * and enable output, we would generate a stop condition. */ +#if 0 i2c_data(I2C_DATA_LOW); /* @@ -292,6 +298,7 @@ i2c_getack(void) */ i2c_enable(); i2c_dir_out(); +#endif i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_HIGH_TIME/4); /* @@ -375,6 +382,121 @@ i2c_sendnack(void) /*#--------------------------------------------------------------------------- *# +*# FUNCTION NAME: i2c_write +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_write(unsigned char theSlave, void *data, size_t nbytes) +{ + int error, cntr = 3; + unsigned char bytes_wrote = 0; + unsigned char value; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * send data + */ + for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) { + memcpy(&value, data + bytes_wrote, sizeof value); + i2c_outbyte(value); + /* + * now it's time to wait for ack + */ + if (!i2c_getack()) + error |= 4; + } + /* + * end byte stream + */ + i2c_stop(); + + } while (error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_read +*# +*# DESCRIPTION : Reads a value from an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_read(unsigned char theSlave, void *data, size_t nbytes) +{ + unsigned char b = 0; + unsigned char bytes_read = 0; + int error, cntr = 3; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + memset(data, 0, nbytes); + /* + * generate start condition + */ + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave | 0x01)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * fetch data + */ + for (bytes_read = 0; bytes_read < nbytes; bytes_read++) { + b = i2c_inbyte(); + memcpy(data + bytes_read, &b, sizeof b); + + if (bytes_read < (nbytes - 1)) + i2c_sendack(); + } + /* + * last received byte needs to be nacked + * instead of acked + */ + i2c_sendnack(); + /* + * end sequence + */ + i2c_stop(); + } while (error && cntr--); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# *# FUNCTION NAME: i2c_writereg *# *# DESCRIPTION : Writes a value to an I2C device @@ -387,12 +509,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); i2c_start(); /* @@ -427,15 +547,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, * end byte stream */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); - } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); + spin_unlock_irqrestore(&i2c_lock, flags); + return -error; } @@ -453,13 +570,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; /* - * we don't like to be interrupted - */ - local_irq_save(flags); - /* * generate start condition */ i2c_start(); @@ -482,7 +597,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * now it's time to wait for ack */ if(!i2c_getack()) - error = 1; + error |= 2; /* * repeat start condition */ @@ -496,7 +611,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * wait for ack */ if(!i2c_getack()) - error = 1; + error |= 4; /* * fetch register */ @@ -510,13 +625,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * end sequence */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while(error && cntr--); + spin_unlock_irqrestore(&i2c_lock, flags); + return b; } @@ -540,7 +653,7 @@ i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { - return -EINVAL; + return -ENOTTY; } switch (_IOC_NR(cmd)) { @@ -580,31 +693,52 @@ static const struct file_operations i2c_fops = { .release = i2c_release, }; -int __init -i2c_init(void) +static int __init i2c_init(void) { - int res; + static int res; + static int first = 1; - /* Setup and enable the Port B I2C interface */ + if (!first) + return res; - crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); - crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + first = 0; + + /* Setup and enable the DATA and CLK pins */ + + res = crisv32_io_get_name(&cris_i2c_data, + CONFIG_ETRAX_V32_I2C_DATA_PORT); + if (res < 0) + return res; + + res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT); + crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out); + + return res; +} + + +static int __init i2c_register(void) +{ + int res; + + res = i2c_init(); + if (res < 0) + return res; /* register char device */ res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO + "I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n"); return 0; } - /* this makes sure that i2c_init is called during boot */ - -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h index bfe1a13f9f3..c073cf4ba01 100644 --- a/arch/cris/arch-v32/drivers/i2c.h +++ b/arch/cris/arch-v32/drivers/i2c.h @@ -3,6 +3,8 @@ /* High level I2C actions */ int __init i2c_init(void); +int i2c_write(unsigned char theSlave, void *data, size_t nbytes); +int i2c_read(unsigned char theSlave, void *data, size_t nbytes); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); diff --git a/arch/cris/arch-v32/drivers/iop_fw_load.c b/arch/cris/arch-v32/drivers/iop_fw_load.c index f4bdc1dfa32..3b3857ec1f1 100644 --- a/arch/cris/arch-v32/drivers/iop_fw_load.c +++ b/arch/cris/arch-v32/drivers/iop_fw_load.c @@ -1,5 +1,4 @@ -/* $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 @@ -11,12 +10,13 @@ #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> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/iop/iop_reg_space.h> +#include <hwregs/iop/iop_mpu_macros.h> +#include <hwregs/iop/iop_mpu_defs.h> +#include <hwregs/iop/iop_spu_defs.h> +#include <hwregs/iop/iop_sw_cpu_defs.h> #define IOP_TIMEOUT 100 diff --git a/arch/cris/arch-v32/drivers/mach-a3/Makefile b/arch/cris/arch-v32/drivers/mach-a3/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/mach-a3/gpio.c b/arch/cris/arch-v32/drivers/mach-a3/gpio.c new file mode 100644 index 00000000000..de107dad9f4 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/gpio.c @@ -0,0 +1,984 @@ +/* + * Artec-3 general port I/O device + * + * Copyright (c) 2007 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G, + * port to ETRAX FS. + * Ricard Wanderlof (PWM for Artpec-3) + * + */ + +#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 <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/arch/mach/pinmux.h> + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + +/* The following gio ports on ARTPEC-3 is available: + * pa 32 bits + * pb 32 bits + * pc 16 bits + * each port has a rw_px_dout, r_px_din and rw_px_oe register. + */ + +#define GPIO_MAJOR 120 /* experimental MAJOR number */ + +#define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */ + +#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"; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *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; +}; + +static void gpio_set_alarm(struct gpio_private *priv); +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg); +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg); + + +/* linked list of alarms to check for */ + +static struct gpio_private *alarmlist; + +static int wanted_interrupts; + +static DEFINE_SPINLOCK(gpio_lock); + +#define NUM_PORTS (GPIO_MINOR_LAST+1) +#define GIO_REG_RD_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg) +static unsigned long led_dummy; +static unsigned long port_d_dummy; /* Only input on Artpec-3 */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long port_e_dummy; /* Non existent on Artpec-3 */ +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif + +static 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), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static 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), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static unsigned long changeable_dir[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_DIR, + CONFIG_ETRAX_PB_CHANGEABLE_DIR, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_DIR, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif +}; + +static unsigned long changeable_bits[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_BITS, + CONFIG_ETRAX_PB_CHANGEABLE_BITS, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_BITS, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif +}; + +static 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), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_rw_pv_oe, +#endif +}; + +static void gpio_set_alarm(struct gpio_private *priv) +{ + int bit; + int intr_cfg; + int mask; + int pins; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + intr_cfg = REG_RD_INT(gio, regi_gio, rw_intr_cfg); + pins = REG_RD_INT(gio, regi_gio, rw_intr_pins); + mask = REG_RD_INT(gio, regi_gio, rw_intr_mask) & I2C_INTERRUPT_BITS; + + for (bit = 0; bit < 32; bit++) { + int intr = bit % 8; + int pin = bit / 8; + if (priv->minor < GPIO_MINOR_LEDS) + pin += priv->minor * 4; + else + pin += (priv->minor - 1) * 4; + + if (priv->highalarm & (1<<bit)) { + intr_cfg |= (regk_gio_hi << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } else if (priv->lowalarm & (1<<bit)) { + intr_cfg |= (regk_gio_lo << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } + } + + REG_WR_INT(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR_INT(gio, regi_gio, rw_intr_pins, pins); + REG_WR_INT(gio, regi_gio, rw_intr_mask, mask); + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct gpio_private *priv = file->private_data; + unsigned long data; + unsigned long tmp; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return 0; + + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor <= GPIO_MINOR_D) { + data = readl(data_in[priv->minor]); + REG_WR_INT(gio, regi_gio, rw_ack_intr, wanted_interrupts); + tmp = REG_RD_INT(gio, regi_gio, rw_intr_mask); + tmp &= I2C_INTERRUPT_BITS; + tmp |= wanted_interrupts; + REG_WR_INT(gio, regi_gio, rw_intr_mask, tmp); + } else + return 0; + + if ((data & priv->highalarm) || (~data & priv->lowalarm)) + mask = POLLIN|POLLRDNORM; + + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +static irqreturn_t gpio_interrupt(int irq, void *dev_id) +{ + reg_gio_rw_intr_mask intr_mask; + reg_gio_r_masked_intr masked_intr; + reg_gio_rw_ack_intr ack_intr; + unsigned long flags; + unsigned long tmp; + unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif + + /* Find what PA interrupts are active */ + masked_intr = REG_RD(gio, regi_gio, r_masked_intr); + tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); + + /* Find those that we have enabled */ + spin_lock_irqsave(&gpio_lock, flags); + tmp &= wanted_interrupts; + spin_unlock_irqrestore(&gpio_lock, flags); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + + /* Ack them */ + ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); + REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); + + /* 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; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + return IRQ_RETVAL(tmp); +} + +static void gpio_write_bit(unsigned long *port, unsigned char data, int bit, + unsigned char clk_mask, unsigned char data_mask) +{ + unsigned long shadow = readl(port) & ~clk_mask; + writel(shadow, port); + if (data & 1 << bit) + shadow |= data_mask; + else + shadow &= ~data_mask; + writel(shadow, port); + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + shadow |= clk_mask; + writel(shadow, port); +} + +static void gpio_write_byte(struct gpio_private *priv, unsigned long *port, + unsigned char data) +{ + int i; + + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); +} + + +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; + ssize_t retval = count; + /* Only bits 0-7 may be used for write operations but allow all + devices except leds... */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) + return -EFAULT; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (priv->clk_mask == 0 || priv->data_mask == 0) + return -EPERM; + + D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " + "msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + spin_lock_irqsave(&gpio_lock, flags); + + while (count--) + gpio_write_byte(priv, data_out[priv->minor], *buf++); + + spin_unlock_irqrestore(&gpio_lock, flags); + return retval; +} + +static int gpio_open(struct inode *inode, struct file *filp) +{ + struct gpio_private *priv; + int p = iminor(inode); + + if (p > GPIO_MINOR_LAST_PWM || + (p > GPIO_MINOR_LAST && p < GPIO_MINOR_PWM0)) + return -EINVAL; + + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + priv->minor = p; + filp->private_data = priv; + + /* initialize the io/alarm struct, not for PWM ports though */ + if (p <= GPIO_MINOR_LAST) { + + priv->clk_mask = 0; + priv->data_mask = 0; + priv->highalarm = 0; + priv->lowalarm = 0; + + init_waitqueue_head(&priv->alarm_wq); + + /* link it into our alarmlist */ + spin_lock_irq(&gpio_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&gpio_lock); + } + + return 0; +} + +static int gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p; + struct gpio_private *todel; + /* local copies while updating them: */ + unsigned long a_high, a_low; + + /* prepare to free private structure */ + todel = filp->private_data; + + /* unlink from alarmlist - only for non-PWM ports though */ + if (todel->minor <= GPIO_MINOR_LAST) { + spin_lock_irq(&gpio_lock); + p = alarmlist; + + if (p == todel) + alarmlist = todel->next; + else { + while (p->next != todel) + p = p->next; + p->next = todel->next; + } + + /* Check if there are still any alarms set */ + p = alarmlist; + a_high = 0; + a_low = 0; + while (p) { + if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + a_high |= p->highalarm; + a_low |= p->lowalarm; + } + + p = p->next; + } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variable 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + + spin_unlock_irq(&gpio_lock); + } + kfree(todel); + + 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(). + */ + +inline unsigned long 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; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) & + ~(arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + + if (priv->minor == GPIO_MINOR_C) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif + else + dir_shadow ^= 0xFFFFFFFF; /* PA, PB and PD 32 bits */ + + return dir_shadow; + +} /* setget_input */ + +static inline unsigned long setget_output(struct gpio_private *priv, + unsigned long arg) +{ + unsigned long flags; + unsigned long dir_shadow; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) | + (arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + return dir_shadow; +} /* setget_output */ + +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 = file->private_data; + + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) + return -ENOTTY; + + /* Check for special ioctl handlers first */ + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif + + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return gpio_pwm_ioctl(priv, cmd, arg); + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + /* Read the port. */ + return readl(data_in[priv->minor]); + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) | + (arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + gpio_set_alarm(priv); + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + return readl(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); + + 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: + { + int res = -EPERM; + unsigned long dir_shadow, clk_mask, data_mask, write_msb; + + clk_mask = arg & 0xFF; + data_mask = (arg >> 8) & 0xFF; + write_msb = (arg >> 16) & 0x01; + + /* Check if we're allowed to change the bits and + * the direction is correct + */ + spin_lock_irqsave(&gpio_lock, flags); + dir_shadow = readl(dir_oe[priv->minor]); + if ((clk_mask & changeable_bits[priv->minor]) && + (data_mask & changeable_bits[priv->minor]) && + (clk_mask & dir_shadow) && + (data_mask & dir_shadow)) { + priv->clk_mask = clk_mask; + priv->data_mask = data_mask; + priv->write_msb = write_msb; + res = 0; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + return res; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = readl(data_in[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + val = *data_out[priv->minor]; + if (copy_to_user((void __user *)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, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)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, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) | + (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = readl(dir_oe[priv->minor]); + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read & ~readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~readl(dir_oe[priv->minor]); + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + 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; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +static int gpio_pwm_set_mode(unsigned long arg, int pwm_port) +{ + int pinmux_pwm = pinmux_pwm0 + pwm_port; + int mode; + reg_gio_rw_pwm0_ctrl rw_pwm_ctrl = { + .ccd_val = 0, + .ccd_override = regk_gio_no, + .mode = regk_gio_no + }; + int allocstatus; + + if (get_user(mode, &((struct io_pwm_set_mode *) arg)->mode)) + return -EFAULT; + rw_pwm_ctrl.mode = mode; + if (mode != PWM_OFF) + allocstatus = crisv32_pinmux_alloc_fixed(pinmux_pwm); + else + allocstatus = crisv32_pinmux_dealloc_fixed(pinmux_pwm); + if (allocstatus) + return allocstatus; + REG_WRITE(reg_gio_rw_pwm0_ctrl, REG_ADDR(gio, regi_gio, rw_pwm0_ctrl) + + 12 * pwm_port, rw_pwm_ctrl); + return 0; +} + +static int gpio_pwm_set_period(unsigned long arg, int pwm_port) +{ + struct io_pwm_set_period periods; + reg_gio_rw_pwm0_var rw_pwm_widths; + + if (copy_from_user(&periods, (void __user *)arg, sizeof(periods))) + return -EFAULT; + if (periods.lo > 8191 || periods.hi > 8191) + return -EINVAL; + rw_pwm_widths.lo = periods.lo; + rw_pwm_widths.hi = periods.hi; + REG_WRITE(reg_gio_rw_pwm0_var, REG_ADDR(gio, regi_gio, rw_pwm0_var) + + 12 * pwm_port, rw_pwm_widths); + return 0; +} + +static int gpio_pwm_set_duty(unsigned long arg, int pwm_port) +{ + unsigned int duty; + reg_gio_rw_pwm0_data rw_pwm_duty; + + if (get_user(duty, &((struct io_pwm_set_duty *) arg)->duty)) + return -EFAULT; + if (duty > 255) + return -EINVAL; + rw_pwm_duty.data = duty; + REG_WRITE(reg_gio_rw_pwm0_data, REG_ADDR(gio, regi_gio, rw_pwm0_data) + + 12 * pwm_port, rw_pwm_duty); + return 0; +} + +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg) +{ + int pwm_port = priv->minor - GPIO_MINOR_PWM0; + + switch (_IOC_NR(cmd)) { + case IO_PWM_SET_MODE: + return gpio_pwm_set_mode(arg, pwm_port); + case IO_PWM_SET_PERIOD: + return gpio_pwm_set_period(arg, pwm_port); + case IO_PWM_SET_DUTY: + return gpio_pwm_set_duty(arg, pwm_port); + default: + return -EINVAL; + } + return 0; +} + +static const struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void __init virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); +} +#endif + +/* main driver initialization routine, called from mem.c */ + +static int __init gpio_init(void) +{ + int res; + + printk(KERN_INFO "ETRAX FS GPIO driver v2.7, (c) 2003-2008 " + "Axis Communications AB\n"); + + /* 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 */ + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + int res2 = request_irq(GIO_INTR_VECT, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio", &alarmlist); + if (res2) { + printk(KERN_ERR "err: irq for gpio\n"); + return res2; + } + + /* No IRQs by default. */ + REG_WR_INT(gio, regi_gio, rw_intr_pins, 0); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif + + 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/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c new file mode 100644 index 00000000000..01ed0be2d0d --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c @@ -0,0 +1,180 @@ +/* + * arch/cris/arch-v32/drivers/nandflash.c + * + * Copyright (c) 2007 + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/arch/memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/pio_defs.h> +#include <pinmux.h> +#include <asm/io.h> + +#define MANUAL_ALE_CLE_CONTROL 1 + +#define regf_ALE a0 +#define regf_CLE a1 +#define regf_NCE ce0_n + +#define CLE_BIT 10 +#define ALE_BIT 11 +#define CE_BIT 12 + +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +static struct mtd_info *crisv32_mtd; +/* + * hardware specific access to control-lines + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + unsigned long flags; + reg_pio_rw_dout dout; + struct nand_chip *this = mtd->priv; + + local_irq_save(flags); + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(pio, regi_pio, rw_dout); + dout.regf_NCE = (ctrl & NAND_NCE) ? 0 : 1; + +#if !MANUAL_ALE_CLE_CONTROL + if (ctrl & NAND_ALE) { + /* A0 = ALE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access1); + } else if (ctrl & NAND_CLE) { + /* A1 = CLE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access2); + } else { + /* A1 = CLE and A0 = ALE low */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access0); + } +#else + + dout.regf_CLE = (ctrl & NAND_CLE) ? 1 : 0; + dout.regf_ALE = (ctrl & NAND_ALE) ? 1 : 0; +#endif + REG_WR(pio, regi_pio, rw_dout, dout); + } + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + + local_irq_restore(flags); +} + +/* +* read device ready pin +*/ +static int crisv32_device_ready(struct mtd_info *mtd) +{ + reg_pio_r_din din = REG_RD(pio, regi_pio, r_din); + return din.rdy; +} + +/* + * Main initialization routine + */ +struct mtd_info *__init crisv32_nand_flash_probe(void) +{ + void __iomem *read_cs; + void __iomem *write_cs; + + struct mtd_info_wrapper *wrapper; + struct nand_chip *this; + int err = 0; + + reg_pio_rw_man_ctrl man_ctrl = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_oe oe = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_dout dout = { .regf_NCE = 1 }; + + /* Allocate pio pins to pio */ + crisv32_pinmux_alloc_fixed(pinmux_pio); + /* Set up CE, ALE, CLE (ce0_n, a0, a1) for manual control and output */ + REG_WR(pio, regi_pio, rw_man_ctrl, man_ctrl); + REG_WR(pio, regi_pio, rw_dout, dout); + REG_WR(pio, regi_pio, rw_oe, oe); + + /* Allocate memory for MTD device structure and private data */ + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); + err = -ENOMEM; + return NULL; + } + + read_cs = write_cs = (void __iomem *)REG_ADDR(pio, regi_pio, + rw_io_access0); + + /* Get pointer to private data */ + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; + + /* Link the private data with the MTD structure */ + crisv32_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = read_cs; + this->IO_ADDR_W = write_cs; + this->cmd_ctrl = crisv32_hwcontrol; + this->dev_ready = crisv32_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + + /* Enable the following for a flash based bad block table */ + /* this->options = NAND_USE_FLASH_BBT; */ + + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { + err = -ENXIO; + goto out_mtd; + } + + return crisv32_mtd; + +out_mtd: + kfree(wrapper); + return NULL; +} + diff --git a/arch/cris/arch-v32/drivers/mach-fs/Makefile b/arch/cris/arch-v32/drivers/mach-fs/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-fs/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/gpio.c b/arch/cris/arch-v32/drivers/mach-fs/gpio.c index d82c5c56113..7863fd4efc2 100644 --- a/arch/cris/arch-v32/drivers/gpio.c +++ b/arch/cris/arch-v32/drivers/mach-fs/gpio.c @@ -1,68 +1,15 @@ -/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ - * +/* * ETRAX CRISv32 general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 1999-2006 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G, * port to ETRAX FS. * - * $Log: gpio.c,v $ - * Revision 1.16 2005/06/19 17:06:49 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.15 2005/05/25 08:22:20 starvik - * Changed GPIO port order to fit packages/devices/axis-2.4. - * - * Revision 1.14 2005/04/24 18:35:08 starvik - * Updated with final register headers. - * - * Revision 1.13 2005/03/15 15:43:00 starvik - * dev_id needs to be supplied for shared IRQs. - * - * Revision 1.12 2005/03/10 17:12:00 starvik - * Protect alarm list with spinlock. - * - * Revision 1.11 2005/01/05 06:08:59 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.10 2004/11/19 08:38:31 starvik - * Removed old crap. - * - * Revision 1.9 2004/05/14 07:58:02 starvik - * Merge of changes from 2.4 - * - * Revision 1.8 2003/09/11 07:29:50 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.7 2003/07/10 13:25:46 starvik - * Compiles for 2.5.74 - * Lindented ethernet.c - * - * Revision 1.6 2003/07/04 08:27:46 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/10 08:26:37 johana - * Etrax -> ETRAX CRISv32 - * - * Revision 1.4 2003/06/05 14:22:48 johana - * Initialise some_alarms. - * - * Revision 1.3 2003/06/05 10:15:46 johana - * New INTR_VECT macros. - * Enable interrupts in global config. - * - * Revision 1.2 2003/06/03 15:52:50 johana - * Initial CRIS v32 version. - * - * Revision 1.1 2003/06/03 08:53:15 johana - * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. - * */ - #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> @@ -77,14 +24,20 @@ #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 <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> #include <asm/io.h> #include <asm/system.h> #include <asm/irq.h> +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + /* The following gio ports on ETRAX FS is available: * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge * pb 18 bits @@ -100,7 +53,12 @@ #if 0 static int dp_cnt; -#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) +#define DP(x) \ + do { \ + dp_cnt++; \ + if (dp_cnt % 1000 == 0) \ + x; \ + } while (0) #else #define DP(x) #endif @@ -111,13 +69,18 @@ static char gpio_name[] = "etrax gpio"; static wait_queue_head_t *gpio_wq; #endif +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + 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); +static unsigned int gpio_poll(struct file *filp, + struct poll_table_struct *wait); /* private data per open() of this driver */ @@ -136,18 +99,25 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -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 int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_high_alarms; +static unsigned long gpio_pa_low_alarms; 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 ) +#define GIO_REG_RD_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) unsigned long led_dummy; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pa_dout), @@ -156,6 +126,9 @@ static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_dout), GIO_REG_WR_ADDR(rw_pd_dout), GIO_REG_WR_ADDR(rw_pe_dout), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static volatile unsigned long *data_in[NUM_PORTS] = { @@ -165,6 +138,9 @@ static volatile unsigned long *data_in[NUM_PORTS] = { GIO_REG_RD_ADDR(r_pc_din), GIO_REG_RD_ADDR(r_pd_din), GIO_REG_RD_ADDR(r_pe_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static unsigned long changeable_dir[NUM_PORTS] = { @@ -174,6 +150,9 @@ static unsigned long changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_DIR, CONFIG_ETRAX_PD_CHANGEABLE_DIR, CONFIG_ETRAX_PE_CHANGEABLE_DIR, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif }; static unsigned long changeable_bits[NUM_PORTS] = { @@ -183,6 +162,9 @@ static unsigned long changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_BITS, CONFIG_ETRAX_PD_CHANGEABLE_BITS, CONFIG_ETRAX_PE_CHANGEABLE_BITS, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif }; static volatile unsigned long *dir_oe[NUM_PORTS] = { @@ -192,13 +174,14 @@ static volatile unsigned long *dir_oe[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_oe), GIO_REG_WR_ADDR(rw_pd_oe), GIO_REG_WR_ADDR(rw_pe_oe), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_rw_pv_oe, +#endif }; -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; @@ -210,65 +193,50 @@ gpio_poll(struct file *file, 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)); + 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)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_hi; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_hi; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_hi; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_hi; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_hi; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_hi; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_hi; - } - if (tmp & (1 << 7)) { + 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)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_lo; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_lo; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_lo; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_lo; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_lo; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_lo; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_lo; - } - if (tmp & (1 << 7)) { + if (tmp & (1 << 7)) intr_cfg.pa7 = regk_gio_lo; - } REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); local_irq_restore(flags); @@ -277,50 +245,65 @@ gpio_poll(struct file *file, else return 0; - if ((data & priv->highalarm) || - (~data & priv->lowalarm)) { + if ((data & priv->highalarm) || (~data & priv->lowalarm)) mask = POLLIN|POLLRDNORM; - } - DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); return mask; } int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; - int ret = 0; + unsigned long flags; + int ret = 0; + spin_lock_irqsave(&alarm_lock, flags); + priv = alarmlist; while (priv) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + data = (unsigned long)cached_virtual_gpio_read; + else { + data = *data_in[priv->minor]; + if (priv->minor == GPIO_MINOR_A) + priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + } +#else data = *data_in[priv->minor]; +#endif if ((data & priv->highalarm) || (~data & priv->lowalarm)) { - DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); + DP(printk(KERN_DEBUG + "etrax_gpio_wake_up_check %i\n", priv->minor)); wake_up_interruptible(&priv->alarm_wq); - ret = 1; + ret = 1; } priv = priv->next; } - return ret; + spin_unlock_irqrestore(&alarm_lock, flags); + return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_pa_interrupt(int irq, void *dev_id) { reg_gio_rw_intr_mask intr_mask; reg_gio_r_masked_intr masked_intr; reg_gio_rw_ack_intr ack_intr; unsigned long tmp; unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif /* Find what PA interrupts are active */ masked_intr = REG_RD(gio, regi_gio, r_masked_intr); @@ -331,6 +314,17 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); spin_unlock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + /* Ack them */ ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); @@ -339,18 +333,24 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +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; @@ -360,29 +360,31 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, 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) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) return -EFAULT; - } - if (!access_ok(VERIFY_READ, buf, count)) { + 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) { + 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)); + D(printk(KERN_DEBUG "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--) { + for (i = 7; i >= 0; i--) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -395,7 +397,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, local_irq_restore(flags); } } else { - for (i = 0; i <= 7;i++) { + for (i = 0; i <= 7; i++) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -423,18 +425,16 @@ gpio_open(struct inode *inode, struct file *filp) if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; + memset(priv, 0, sizeof(*priv)); priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; priv->clk_mask = 0; priv->data_mask = 0; priv->highalarm = 0; @@ -443,20 +443,30 @@ gpio_open(struct inode *inode, struct file *filp) filp->private_data = (void *)priv; + /* link it into our alarmlist */ + spin_lock_irq(&alarm_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&alarm_lock); + return 0; } static int gpio_release(struct inode *inode, struct file *filp) { - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; + struct gpio_private *p; + struct gpio_private *todel; /* local copies while updating them: */ unsigned long a_high, a_low; unsigned long some_alarms; /* unlink from alarmlist and free the private structure */ + spin_lock_irq(&alarm_lock); + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; + if (p == todel) { alarmlist = todel->next; } else { @@ -468,26 +478,35 @@ gpio_release(struct inode *inode, struct file *filp) kfree(todel); /* Check if there are still any alarms set */ p = alarmlist; - some_alarms = 0; + some_alarms = 0; a_high = 0; a_low = 0; while (p) { if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif a_high |= p->highalarm; a_low |= p->lowalarm; } - if (p->highalarm | p->lowalarm) { + if (p->highalarm | p->lowalarm) some_alarms = 1; - } p = p->next; } - spin_lock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variables 'some_alarms' and 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + some_alarms = 1; + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + gpio_some_alarms = some_alarms; gpio_pa_high_alarms = a_high; gpio_pa_low_alarms = a_low; - spin_unlock(&alarm_lock); + spin_unlock_irq(&alarm_lock); return 0; } @@ -496,7 +515,7 @@ gpio_release(struct inode *inode, struct file *filp) * set alarms to wait for using a subsequent select(). */ -unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) { /* Set direction 0=unchanged 1=input, * return mask with 1=input @@ -512,13 +531,17 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) if (priv->minor == GPIO_MINOR_A) dir_shadow ^= 0xFF; /* Only 8 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif else dir_shadow ^= 0x3FFFF; /* Only 18 bits */ return dir_shadow; } /* setget_input */ -unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg) { unsigned long flags; unsigned long dir_shadow; @@ -542,20 +565,22 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; unsigned long shadow; struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ - // read the port + /* 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 + /* Set changeable bits with a 1 in arg. */ shadow = *data_out[priv->minor]; shadow |= (arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; @@ -563,46 +588,42 @@ gpio_ioctl(struct inode *inode, struct file *file, break; case IO_CLRBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO CLR 2\n"); - // clear changeable bits with a 1 in arg + /* 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 + /* Set alarm when bits with 1 in arg go high. */ priv->highalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_high_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_LOWALARM: - // set alarm when bits with 1 in arg go low + /* Set alarm when bits with 1 in arg go low. */ priv->lowalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_low_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_CLRALARM: - // clear alarm for bits with 1 in arg + /* Clear alarm for bits with 1 in arg. */ priv->highalarm &= ~arg; priv->lowalarm &= ~arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); if (priv->minor == GPIO_MINOR_A) { if (gpio_pa_high_alarms & arg || - gpio_pa_low_alarms & arg) { + gpio_pa_low_alarms & arg) /* Must update the gpio_pa_*alarms masks */ - } + ; } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ @@ -633,8 +654,7 @@ gpio_ioctl(struct inode *inode, struct file *file, 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->data_mask & dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; return -EPERM; @@ -644,34 +664,34 @@ gpio_ioctl(struct inode *inode, struct file *file, 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))) + 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))) + 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))) + 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))) + 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))) + 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))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; default: @@ -684,6 +704,133 @@ gpio_ioctl(struct inode *inode, struct file *file, return 0; } +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int +virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + local_irq_save(flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow |= (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow &= ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + spin_lock(&alarm_lock); + spin_unlock(&alarm_lock); + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = *dir_oe[priv->minor]; + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read; + val &= ~*dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= *dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~*dir_oe[priv->minor]; + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) { @@ -694,8 +841,8 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) 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); + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; default: @@ -705,7 +852,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -714,6 +861,66 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void +virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + gpio_some_alarms = 1; +} +#endif /* main driver initialization routine, called from mem.c */ @@ -721,7 +928,6 @@ static __init int gpio_init(void) { int res; - reg_intr_vect_rw_mask intr_mask; /* do the formalities */ @@ -732,30 +938,30 @@ gpio_init(void) } /* 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"); + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + printk(KERN_INFO "ETRAX FS GPIO driver v2.5, (c) 2003-2007 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. */ - if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) { - printk("err: timer0 irq for gpio\n"); - } - if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"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); + if (request_irq(TIMER0_INTR_VECT, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", &alarmlist)) + printk(KERN_ERR "timer0 irq for gpio\n"); + + if (request_irq(GIO_INTR_VECT, gpio_pa_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", &alarmlist)) + printk(KERN_ERR "PA irq for gpio\n"); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif return res; } diff --git a/arch/cris/arch-v32/drivers/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c index 5ce015c6bb0..aa01b134458 100644 --- a/arch/cris/arch-v32/drivers/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c @@ -4,9 +4,7 @@ * Copyright (c) 2004 * * Derived from drivers/mtd/nand/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $ + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,10 +19,10 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <asm/arch/memmap.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/bif_core_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/bif_core_defs.h> #include <asm/io.h> #define CE_BIT 4 @@ -32,44 +30,65 @@ #define ALE_BIT 6 #define BY_BIT 7 -static struct mtd_info *crisv32_mtd = NULL; +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +/* Bitmask for mtd nand control bits */ +#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE) + + +static struct mtd_info *crisv32_mtd; /* * hardware specific access to control-lines -*/ -static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { unsigned long flags; - reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + reg_gio_rw_pa_dout dout; + struct nand_chip *this = mtd->priv; local_irq_save(flags); - switch(cmd){ - case NAND_CTL_SETCLE: - dout.data |= (1<<CLE_BIT); - break; - case NAND_CTL_CLRCLE: - dout.data &= ~(1<<CLE_BIT); - break; - case NAND_CTL_SETALE: - dout.data |= (1<<ALE_BIT); - break; - case NAND_CTL_CLRALE: - dout.data &= ~(1<<ALE_BIT); - break; - case NAND_CTL_SETNCE: - dout.data |= (1<<CE_BIT); - break; - case NAND_CTL_CLRNCE: - dout.data &= ~(1<<CE_BIT); - break; + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(gio, regi_gio, rw_pa_dout); + dout.data &= ~PIN_BITMASK; + +#if (CE_BIT == 4 && NAND_NCE == 1 && \ + CLE_BIT == 5 && NAND_CLE == 2 && \ + ALE_BIT == 6 && NAND_ALE == 4) + /* Pins in same order as control bits, but shifted. + * Optimize for this case; works for 2.6.18 */ + dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT; +#else + /* the slow way */ + if (!(ctrl & NAND_NCE)) + dout.data |= (1 << CE_BIT); + if (ctrl & NAND_CLE) + dout.data |= (1 << CLE_BIT); + if (ctrl & NAND_ALE) + dout.data |= (1 << ALE_BIT); +#endif + REG_WR(gio, regi_gio, rw_pa_dout, dout); } - REG_WR(gio, regi_gio, rw_pa_dout, dout); + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + local_irq_restore(flags); } /* * read device ready pin */ -int crisv32_device_ready(struct mtd_info *mtd) +static int crisv32_device_ready(struct mtd_info *mtd) { reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din); return ((din.data & (1 << BY_BIT)) >> BY_BIT); @@ -78,21 +97,23 @@ int crisv32_device_ready(struct mtd_info *mtd) /* * Main initialization routine */ -struct mtd_info* __init crisv32_nand_flash_probe (void) +struct mtd_info *__init crisv32_nand_flash_probe(void) { void __iomem *read_cs; void __iomem *write_cs; - reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg); + reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, + rw_grp3_cfg); reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe); + struct mtd_info_wrapper *wrapper; struct nand_chip *this; int err = 0; /* Allocate memory for MTD device structure and private data */ - crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), - GFP_KERNEL); - if (!crisv32_mtd) { - printk ("Unable to allocate CRISv32 NAND MTD device structure.\n"); + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); err = -ENOMEM; return NULL; } @@ -101,45 +122,42 @@ struct mtd_info* __init crisv32_nand_flash_probe (void) write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192); if (!read_cs || !write_cs) { - printk("CRISv32 NAND ioremap failed\n"); + printk(KERN_ERR "CRISv32 NAND ioremap failed\n"); err = -EIO; goto out_mtd; } /* Get pointer to private data */ - this = (struct nand_chip *) (&crisv32_mtd[1]); + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; pa_oe.oe |= 1 << CE_BIT; pa_oe.oe |= 1 << ALE_BIT; pa_oe.oe |= 1 << CLE_BIT; - pa_oe.oe &= ~ (1 << BY_BIT); + pa_oe.oe &= ~(1 << BY_BIT); REG_WR(gio, regi_gio, rw_pa_oe, pa_oe); bif_cfg.gated_csp0 = regk_bif_core_rd; bif_cfg.gated_csp1 = regk_bif_core_wr; REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg); - /* Initialize structures */ - memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info)); - memset((char *) this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ crisv32_mtd->priv = this; /* Set address of NAND IO lines */ this->IO_ADDR_R = read_cs; this->IO_ADDR_W = write_cs; - this->hwcontrol = crisv32_hwcontrol; + this->cmd_ctrl = crisv32_hwcontrol; this->dev_ready = crisv32_device_ready; /* 20 us command delay time */ this->chip_delay = 20; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - this->options = NAND_USE_FLASH_BBT; + /* this->options = NAND_USE_FLASH_BBT; */ - /* Scan to find existence of the device */ - if (nand_scan (crisv32_mtd, 1)) { + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { err = -ENXIO; goto out_ior; } @@ -150,7 +168,7 @@ out_ior: iounmap((void *)read_cs); iounmap((void *)write_cs); out_mtd: - kfree (crisv32_mtd); - return NULL; + kfree(wrapper); + return NULL; } diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c index 6dbd700d3d6..53db3870ba0 100644 --- a/arch/cris/arch-v32/drivers/pcf8563.c +++ b/arch/cris/arch-v32/drivers/pcf8563.c @@ -10,7 +10,7 @@ * 400 kbits/s. The built-in word address register is incremented * automatically after each written or read byte. * - * Copyright (c) 2002-2003, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. @@ -26,6 +26,7 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -37,24 +38,27 @@ #define PCF8563_MAJOR 121 /* Local major number. */ #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.1 $" +#define DRIVER_VERSION "$Revision: 1.17 $" /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -int pcf8563_open(struct inode *, struct file *); -int pcf8563_release(struct inode *, struct file *); + +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, - .ioctl = pcf8563_ioctl, - .open = pcf8563_open, - .release = pcf8563_release, + .ioctl = pcf8563_ioctl }; unsigned char @@ -62,7 +66,7 @@ pcf8563_readreg(int reg) { unsigned char res = rtc_read(reg); - /* The PCF8563 does not return 0 for unimplemented bits */ + /* The PCF8563 does not return 0 for unimplemented bits. */ switch (reg) { case RTC_SECONDS: case RTC_MINUTES: @@ -95,11 +99,6 @@ pcf8563_readreg(int reg) void pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } @@ -114,11 +113,13 @@ get_rtc_time(struct rtc_time *tm) tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); tm->tm_sec &= 0x7F; tm->tm_min &= 0x7F; tm->tm_hour &= 0x3F; @@ -137,8 +138,19 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + /* Initiate the i2c protocol. */ - i2c_init(); + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; + } /* * First of all we need to reset the chip. This is done by @@ -170,24 +182,20 @@ pcf8563_init(void) if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); - return -1; + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - - return 0; + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -200,8 +208,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -211,125 +219,147 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { - return -EFAULT; - } - - return 0; + case RTC_RD_TIME: + { + struct rtc_time tm; + + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; } - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) return -EPERM; -#else - int leap; - int year; - int century; - struct rtc_time tm; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - /* - * Check if tm.tm_year is a leap year. A year is a leap - * year if it is divisible by 4 but not 100, except - * that years divisible by 400 _are_ leap years. - */ - year = tm.tm_year; - leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_wday >= 7) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - case RTC_VLOW_RD: - { - int vl_bit = 0; + if (copy_from_user(&tm, (struct rtc_time *) arg, + sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; - return 0; - } + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + int ret = rtc_read(RTC_SECONDS); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + rtc_write(RTC_SECONDS, (ret & 0x7F)); - return 0; - } + /* Clear the cached value. */ + voltage_low = 0; - default: - return -ENOTTY; + return 0; + } + default: + return -ENOTTY; } return 0; } -int -pcf8563_open(struct inode *inode, struct file *filp) +static int __init pcf8563_register(void) { - return 0; -} + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC " + "device.\n", PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } -int -pcf8563_release(struct inode *inode, struct file *filp) -{ return 0; } -module_init(pcf8563_init); +module_init(pcf8563_register); module_exit(pcf8563_exit); diff --git a/arch/cris/arch-v32/drivers/sync_serial.c b/arch/cris/arch-v32/drivers/sync_serial.c index d581b0a92a3..47c377df6fb 100644 --- a/arch/cris/arch-v32/drivers/sync_serial.c +++ b/arch/cris/arch-v32/drivers/sync_serial.c @@ -1,5 +1,5 @@ /* - * Simple synchronous serial port driver for ETRAX FS. + * Simple synchronous serial port driver for ETRAX FS and Artpec-3. * * Copyright (c) 2005 Axis Communications AB * @@ -21,17 +21,18 @@ #include <linux/spinlock.h> #include <asm/io.h> -#include <asm/arch/dma.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/sser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/hwregs/dma.h> -#include <asm/arch/hwregs/intr_vect_defs.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/reg_map.h> +#include <dma.h> +#include <pinmux.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/sser_defs.h> +#include <hwregs/dma_defs.h> +#include <hwregs/dma.h> +#include <hwregs/intr_vect_defs.h> +#include <hwregs/intr_vect.h> +#include <hwregs/reg_map.h> #include <asm/sync_serial.h> + /* The receiver is a bit tricky beacuse of the continuous stream of data.*/ /* */ /* Three DMA descriptors are linked together. Each DMA descriptor is */ @@ -63,8 +64,10 @@ /* words can be handled */ #define IN_BUFFER_SIZE 12288 #define IN_DESCR_SIZE 256 -#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) -#define OUT_BUFFER_SIZE 4096 +#define NBR_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) + +#define OUT_BUFFER_SIZE 1024*8 +#define NBR_OUT_DESCR 8 #define DEFAULT_FRAME_RATE 0 #define DEFAULT_WORD_RATE 7 @@ -78,6 +81,8 @@ #define DEBUGPOLL(x) #define DEBUGRXINT(x) #define DEBUGTXINT(x) +#define DEBUGTRDMA(x) +#define DEBUGOUTBUF(x) typedef struct sync_port { @@ -97,10 +102,11 @@ typedef struct sync_port int output; int input; - volatile unsigned int out_count; /* Remaining bytes for current transfer */ - unsigned char* outp; /* Current position in out_buffer */ - volatile unsigned char* volatile readp; /* Next byte to be read by application */ - volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + unsigned int in_buffer_size; unsigned int inbufchunk; unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); @@ -108,11 +114,30 @@ typedef struct sync_port unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); struct dma_descr_data* next_rx_desc; struct dma_descr_data* prev_rx_desc; + + /* Pointer to the first available descriptor in the ring, + * unless active_tr_descr == catch_tr_descr and a dma + * transfer is active */ + struct dma_descr_data *active_tr_descr; + + /* Pointer to the first allocated descriptor in the ring */ + struct dma_descr_data *catch_tr_descr; + + /* Pointer to the descriptor with the current end-of-list */ + struct dma_descr_data *prev_tr_descr; int full; - dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); + /* Pointer to the first byte being read by DMA + * or current position in out_buffer if not using DMA. */ + unsigned char *out_rd_ptr; + + /* Number of bytes currently locked for being read by DMA */ + int out_buf_count; + + dma_descr_data in_descr[NBR_IN_DESCR] __attribute__ ((__aligned__(16))); dma_descr_context in_context __attribute__ ((__aligned__(32))); - dma_descr_data out_descr __attribute__ ((__aligned__(16))); + dma_descr_data out_descr[NBR_OUT_DESCR] + __attribute__ ((__aligned__(16))); dma_descr_context out_context __attribute__ ((__aligned__(32))); wait_queue_head_t out_wait_q; wait_queue_head_t in_wait_q; @@ -143,11 +168,11 @@ static ssize_t sync_serial_read(struct file *file, char *buf, #endif static void send_word(sync_port* port); -static void start_dma(struct sync_port *port, const char* data, int count); +static void start_dma_out(struct sync_port *port, const char *data, int count); static void start_dma_in(sync_port* port); #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); #endif #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ @@ -157,22 +182,49 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); #define SYNC_SER_MANUAL #endif #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +#ifdef CONFIG_ETRAXFS /* ETRAX FS */ +#define OUT_DMA_NBR 4 +#define IN_DMA_NBR 5 +#define PINMUX_SSER pinmux_sser0 +#define SYNCSER_INST regi_sser0 +#define SYNCSER_INTR_VECT SSER0_INTR_VECT +#define OUT_DMA_INST regi_dma4 +#define IN_DMA_INST regi_dma5 +#define DMA_OUT_INTR_VECT DMA4_INTR_VECT +#define DMA_IN_INTR_VECT DMA5_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser0 +#else /* Artpec-3 */ +#define OUT_DMA_NBR 6 +#define IN_DMA_NBR 7 +#define PINMUX_SSER pinmux_sser +#define SYNCSER_INST regi_sser +#define SYNCSER_INTR_VECT SSER_INTR_VECT +#define OUT_DMA_INST regi_dma6 +#define IN_DMA_INST regi_dma7 +#define DMA_OUT_INTR_VECT DMA6_INTR_VECT +#define DMA_IN_INTR_VECT DMA7_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser #endif /* The ports */ static struct sync_port ports[]= { { - .regi_sser = regi_sser0, - .regi_dmaout = regi_dma4, - .regi_dmain = regi_dma5, + .regi_sser = SYNCSER_INST, + .regi_dmaout = OUT_DMA_INST, + .regi_dmain = IN_DMA_INST, #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) .use_dma = 1, #else .use_dma = 0, #endif - }, + } +#ifdef CONFIG_ETRAXFS + , + { .regi_sser = regi_sser1, .regi_dmaout = regi_dma6, @@ -183,9 +235,10 @@ static struct sync_port ports[]= .use_dma = 0, #endif } +#endif }; -#define NUMBER_OF_PORTS ARRAY_SIZE(ports) +#define NBR_PORTS ARRAY_SIZE(ports) static const struct file_operations sync_serial_fops = { .owner = THIS_MODULE, @@ -200,19 +253,21 @@ static const struct file_operations sync_serial_fops = { static int __init etrax_sync_serial_init(void) { ports[0].enabled = 0; +#ifdef CONFIG_ETRAXFS ports[1].enabled = 0; - - if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) - { - printk("unable to get major for synchronous serial port\n"); +#endif + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { + printk(KERN_WARNING + "Unable to get major for synchronous serial port\n"); return -EBUSY; } /* Initialize Ports */ #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) - if (crisv32_pinmux_alloc_fixed(pinmux_sser0)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(PINMUX_SSER)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[0].enabled = 1; @@ -220,33 +275,40 @@ static int __init etrax_sync_serial_init(void) #endif #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) - if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[1].enabled = 1; initialize_port(1); #endif - printk("ETRAX FS synchronous serial port driver\n"); +#ifdef CONFIG_ETRAXFS + printk(KERN_INFO "ETRAX FS synchronous serial port driver\n"); +#else + printk(KERN_INFO "Artpec-3 synchronous serial port driver\n"); +#endif return 0; } static void __init initialize_port(int portnbr) { - struct sync_port* port = &ports[portnbr]; + int __attribute__((unused)) i; + struct sync_port *port = &ports[portnbr]; reg_sser_rw_cfg cfg = {0}; reg_sser_rw_frm_cfg frm_cfg = {0}; reg_sser_rw_tr_cfg tr_cfg = {0}; reg_sser_rw_rec_cfg rec_cfg = {0}; - DEBUG(printk("Init sync serial port %d\n", portnbr)); + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); port->port_nbr = portnbr; port->init_irqs = 1; - port->outp = port->out_buffer; + port->out_rd_ptr = port->out_buffer; + port->out_buf_count = 0; + port->output = 1; port->input = 0; @@ -255,7 +317,7 @@ static void __init initialize_port(int portnbr) port->in_buffer_size = IN_BUFFER_SIZE; port->inbufchunk = IN_DESCR_SIZE; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR-1]; port->prev_rx_desc->eol = 1; init_waitqueue_head(&port->out_wait_q); @@ -286,8 +348,13 @@ static void __init initialize_port(int portnbr) tr_cfg.sample_size = 7; tr_cfg.sh_dir = regk_sser_msbfirst; tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; +#if 0 tr_cfg.rate_ctrl = regk_sser_bulk; tr_cfg.data_pin_use = regk_sser_dout; +#else + tr_cfg.rate_ctrl = regk_sser_iso; + tr_cfg.data_pin_use = regk_sser_dout; +#endif tr_cfg.bulk_wspace = 1; REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -296,6 +363,27 @@ static void __init initialize_port(int portnbr) rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; rec_cfg.fifo_thr = regk_sser_inf; REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + +#ifdef SYNC_SER_DMA + /* Setup the descriptor ring for dma out/transmit. */ + for (i = 0; i < NBR_OUT_DESCR; i++) { + port->out_descr[i].wait = 0; + port->out_descr[i].intr = 1; + port->out_descr[i].eol = 0; + port->out_descr[i].out_eop = 0; + port->out_descr[i].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[i+1]); + } + + /* Create a ring from the list. */ + port->out_descr[NBR_OUT_DESCR-1].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[0]); + + /* Setup context for traversing the ring. */ + port->active_tr_descr = &port->out_descr[0]; + port->prev_tr_descr = &port->out_descr[NBR_OUT_DESCR-1]; + port->catch_tr_descr = &port->out_descr[0]; +#endif } static inline int sync_data_avail(struct sync_port *port) @@ -311,7 +399,7 @@ static inline int sync_data_avail(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->in_buffer_size - (start - end); @@ -331,7 +419,7 @@ static inline int sync_data_avail_to_end(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->flip + port->in_buffer_size - start; @@ -341,66 +429,69 @@ static inline int sync_data_avail_to_end(struct sync_port *port) static int sync_serial_open(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; - DEBUG(printk("Open sync serial port %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { - DEBUG(printk("Invalid minor %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; /* Allow open this device twice (assuming one reader and one writer) */ if (port->busy == 2) { - DEBUG(printk("Device is busy.. \n")); + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); return -EBUSY; } + + if (port->init_irqs) { if (port->use_dma) { - if (port == &ports[0]){ + if (port == &ports[0]) { #ifdef SYNC_SER_DMA - if(request_irq(DMA4_INTR_VECT, - tr_interrupt, - 0, - "synchronous serial 0 dma tr", - &ports[0])) { + if (request_irq(DMA_OUT_INTR_VECT, + tr_interrupt, + 0, + "synchronous serial 0 dma tr", + &ports[0])) { printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if(request_irq(DMA5_INTR_VECT, - rx_interrupt, - 0, - "synchronous serial 1 dma rx", - &ports[0])) { - free_irq(DMA4_INTR_VECT, &port[0]); + } else if (request_irq(DMA_IN_INTR_VECT, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR, - "synchronous serial 0 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(OUT_DMA_NBR, + "synchronous serial 0 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR, - "synchronous serial 0 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - crisv32_free_dma(SYNC_SER0_TX_DMA_NBR); - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(IN_DMA_NBR, + "synchronous serial 0 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + crisv32_free_dma(OUT_DMA_NBR); + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); return -EBUSY; } #endif } - else if (port == &ports[1]){ +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { #ifdef SYNC_SER_DMA if (request_irq(DMA6_INTR_VECT, tr_interrupt, @@ -417,20 +508,22 @@ static int sync_serial_open(struct inode *inode, struct file *file) free_irq(DMA6_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR, - "synchronous serial 1 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { - free_irq(21, &ports[1]); - free_irq(20, &ports[1]); + } else if (crisv32_request_dma( + SYNC_SER1_TX_DMA_NBR, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { + free_irq(DMA6_INTR_VECT, &ports[1]); + free_irq(DMA7_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR, - "synchronous serial 3 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { + } else if (crisv32_request_dma( + SYNC_SER1_RX_DMA_NBR, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { crisv32_free_dma(SYNC_SER1_TX_DMA_NBR); free_irq(DMA6_INTR_VECT, &ports[1]); free_irq(DMA7_INTR_VECT, &ports[1]); @@ -439,14 +532,14 @@ static int sync_serial_open(struct inode *inode, struct file *file) } #endif } - +#endif /* Enable DMAs */ REG_WR(dma, port->regi_dmain, rw_cfg, cfg); REG_WR(dma, port->regi_dmaout, rw_cfg, cfg); /* Enable DMA IRQs */ REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask); REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask); - /* Set up wordsize = 2 for DMAs. */ + /* Set up wordsize = 1 for DMAs. */ DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1); DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1); @@ -455,7 +548,7 @@ static int sync_serial_open(struct inode *inode, struct file *file) } else { /* !port->use_dma */ #ifdef SYNC_SER_MANUAL if (port == &ports[0]) { - if (request_irq(SSER0_INTR_VECT, + if (request_irq(SYNCSER_INTR_VECT, manual_interrupt, 0, "synchronous serial manual irq", @@ -463,7 +556,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) printk("Can't allocate sync serial manual irq"); return -EBUSY; } - } else if (port == &ports[1]) { + } +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { if (request_irq(SSER1_INTR_VECT, manual_interrupt, 0, @@ -473,11 +568,13 @@ static int sync_serial_open(struct inode *inode, struct file *file) return -EBUSY; } } +#endif port->init_irqs = 0; #else panic("sync_serial: Manual mode not supported.\n"); #endif /* SYNC_SER_MANUAL */ } + } /* port->init_irqs */ port->busy++; @@ -487,9 +584,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) static int sync_serial_release(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -506,17 +603,37 @@ static unsigned int sync_serial_poll(struct file *file, poll_table *wait) { int dev = iminor(file->f_path.dentry->d_inode); unsigned int mask = 0; - sync_port* port; + sync_port *port; DEBUGPOLL( static unsigned int prev_mask = 0; ); port = &ports[dev]; + + if (!port->started) { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_rec_cfg rec_cfg = + REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + rec_cfg.rec_en = port->input; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + poll_wait(file, &port->out_wait_q, wait); poll_wait(file, &port->in_wait_q, wait); - /* Some room to write */ - if (port->out_count < OUT_BUFFER_SIZE) + + /* No active transfer, descriptors are available */ + if (port->output && !port->tr_running) + mask |= POLLOUT | POLLWRNORM; + + /* Descriptor and buffer space available. */ + if (port->output && + port->active_tr_descr != port->catch_tr_descr && + port->out_buf_count < OUT_BUFFER_SIZE) mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ - if (sync_data_avail(port) >= port->inbufchunk) + if (port->input && sync_data_avail(port) >= port->inbufchunk) mask |= POLLIN | POLLRDNORM; DEBUGPOLL(if (mask != prev_mask) @@ -531,15 +648,16 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int return_val = 0; + int dma_w_size = regk_dma_set_w_size1; int dev = iminor(file->f_path.dentry->d_inode); - sync_port* port; + sync_port *port; reg_sser_rw_tr_cfg tr_cfg; reg_sser_rw_rec_cfg rec_cfg; reg_sser_rw_frm_cfg frm_cfg; reg_sser_rw_cfg gen_cfg; reg_sser_rw_intr_mask intr_mask; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -1; @@ -558,61 +676,81 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case SSP_SPEED: if (GET_SPEED(arg) == CODEC) { + unsigned int freq; + gen_cfg.base_freq = regk_sser_f32; - /* FREQ = 0 => 4 MHz => clk_div = 7*/ - gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg)); - } - else - { + + /* Clock divider will internally be + * gen_cfg.clk_div + 1. + */ + + freq = GET_FREQ(arg); + switch (freq) { + case FREQ_32kHz: + case FREQ_64kHz: + case FREQ_128kHz: + case FREQ_256kHz: + gen_cfg.clk_div = 125 * + (1 << (freq - FREQ_256kHz)) - 1; + break; + case FREQ_512kHz: + gen_cfg.clk_div = 62; + break; + case FREQ_1MHz: + case FREQ_2MHz: + case FREQ_4MHz: + gen_cfg.clk_div = 8 * (1 << freq) - 1; + break; + } + } else { gen_cfg.base_freq = regk_sser_f29_493; - switch (GET_SPEED(arg)) - { - case SSP150: - gen_cfg.clk_div = 29493000 / (150 * 8) - 1; - break; - case SSP300: - gen_cfg.clk_div = 29493000 / (300 * 8) - 1; - break; - case SSP600: - gen_cfg.clk_div = 29493000 / (600 * 8) - 1; - break; - case SSP1200: - gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; - break; - case SSP2400: - gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; - break; - case SSP4800: - gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; - break; - case SSP9600: - gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; - break; - case SSP19200: - gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; - break; - case SSP28800: - gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; - break; - case SSP57600: - gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; - break; - case SSP115200: - gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; - break; - case SSP230400: - gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; - break; - case SSP460800: - gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; - break; - case SSP921600: - gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; - break; - case SSP3125000: - gen_cfg.base_freq = regk_sser_f100; - gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; - break; + switch (GET_SPEED(arg)) { + case SSP150: + gen_cfg.clk_div = 29493000 / (150 * 8) - 1; + break; + case SSP300: + gen_cfg.clk_div = 29493000 / (300 * 8) - 1; + break; + case SSP600: + gen_cfg.clk_div = 29493000 / (600 * 8) - 1; + break; + case SSP1200: + gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; + break; + case SSP2400: + gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; + break; + case SSP4800: + gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; + break; + case SSP9600: + gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; + break; + case SSP19200: + gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; + break; + case SSP28800: + gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; + break; + case SSP57600: + gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; + break; + case SSP115200: + gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; + break; + case SSP230400: + gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; + break; + case SSP460800: + gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; + break; + case SSP921600: + gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; + break; + case SSP3125000: + gen_cfg.base_freq = regk_sser_f100; + gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; + break; } } @@ -625,46 +763,60 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case MASTER_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.out_on = regk_sser_tr; + frm_cfg.frame_pin_dir = regk_sser_out; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; default: spin_unlock_irq(&port->lock); return -EINVAL; - } if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)) intr_mask.rdav = regk_sser_yes; break; case SSP_FRAME_SYNC: - if (arg & NORMAL_SYNC) + if (arg & NORMAL_SYNC) { + frm_cfg.rec_delay = 1; frm_cfg.tr_delay = 1; + } else if (arg & EARLY_SYNC) - frm_cfg.tr_delay = 0; + frm_cfg.rec_delay = frm_cfg.tr_delay = 0; + else if (arg & SECOND_WORD_SYNC) { + frm_cfg.rec_delay = 7; + frm_cfg.tr_delay = 1; + } tr_cfg.bulk_wspace = frm_cfg.tr_delay; frm_cfg.early_wend = regk_sser_yes; @@ -680,9 +832,11 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & SYNC_OFF) frm_cfg.frame_pin_use = regk_sser_gio0; - if (arg & WORD_SIZE_8) + dma_w_size = regk_dma_set_w_size2; + if (arg & WORD_SIZE_8) { rec_cfg.sample_size = tr_cfg.sample_size = 7; - else if (arg & WORD_SIZE_12) + dma_w_size = regk_dma_set_w_size1; + } else if (arg & WORD_SIZE_12) rec_cfg.sample_size = tr_cfg.sample_size = 11; else if (arg & WORD_SIZE_16) rec_cfg.sample_size = tr_cfg.sample_size = 15; @@ -696,10 +850,13 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & BIT_ORDER_LSB) rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst; - if (arg & FLOW_CONTROL_ENABLE) + if (arg & FLOW_CONTROL_ENABLE) { + frm_cfg.status_pin_use = regk_sser_frm; rec_cfg.fifo_thr = regk_sser_thr16; - else if (arg & FLOW_CONTROL_DISABLE) + } else if (arg & FLOW_CONTROL_DISABLE) { + frm_cfg.status_pin_use = regk_sser_gio0; rec_cfg.fifo_thr = regk_sser_inf; + } if (arg & CLOCK_NOT_GATED) gen_cfg.gate_clk = regk_sser_no; @@ -726,9 +883,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, break; case SSP_OPOLARITY: if (arg & CLOCK_NORMAL) - gen_cfg.out_clk_pol = regk_sser_neg; - else if (arg & CLOCK_INVERT) gen_cfg.out_clk_pol = regk_sser_pos; + else if (arg & CLOCK_INVERT) + gen_cfg.out_clk_pol = regk_sser_neg; if (arg & FRAME_NORMAL) frm_cfg.level = regk_sser_pos_hi; @@ -770,10 +927,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, } - if (port->started) - { - tr_cfg.tr_en = port->output; + if (port->started) { rec_cfg.rec_en = port->input; + gen_cfg.en = (port->output | port->input); } REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -782,138 +938,145 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + + if (cmd == SSP_FRAME_SYNC && (arg & (WORD_SIZE_8 | WORD_SIZE_12 | + WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) { + int en = gen_cfg.en; + gen_cfg.en = 0; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + /* ##### Should DMA be stoped before we change dma size? */ + DMA_WR_CMD(port->regi_dmain, dma_w_size); + DMA_WR_CMD(port->regi_dmaout, dma_w_size); + gen_cfg.en = en; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + } + spin_unlock_irq(&port->lock); return return_val; } -static ssize_t sync_serial_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) +/* NOTE: sync_serial_write does not support concurrency */ +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { int dev = iminor(file->f_path.dentry->d_inode); DECLARE_WAITQUEUE(wait, current); - sync_port *port; - unsigned long c, c1; - unsigned long free_outp; - unsigned long outp; - unsigned long out_buffer; + struct sync_port *port; + int trunc_count; unsigned long flags; + int bytes_free; + int out_buf_count; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { + unsigned char *rd_ptr; /* First allocated byte in the buffer */ + unsigned char *wr_ptr; /* First free byte in the buffer */ + unsigned char *buf_stop_ptr; /* Last byte + 1 */ + + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; - DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); - /* Space to end of buffer */ - /* - * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE - * outp^ +out_count - ^free_outp - * out_buffer 45<- c ->0123OUT_BUFFER_SIZE - * +out_count outp^ - * free_outp - * + /* |<- OUT_BUFFER_SIZE ->| + * |<- out_buf_count ->| + * |<- trunc_count ->| ...->| + * ______________________________________________________ + * | free | data | free | + * |_________|___________________|________________________| + * ^ rd_ptr ^ wr_ptr */ + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu a: %p c: %p\n", + port->port_nbr, count, port->active_tr_descr, + port->catch_tr_descr)); /* Read variables that may be updated by interrupts */ spin_lock_irqsave(&port->lock, flags); - count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count; - outp = (unsigned long)port->outp; - free_outp = outp + port->out_count; + rd_ptr = port->out_rd_ptr; + out_buf_count = port->out_buf_count; spin_unlock_irqrestore(&port->lock, flags); - out_buffer = (unsigned long)port->out_buffer; - /* Find out where and how much to write */ - if (free_outp >= out_buffer + OUT_BUFFER_SIZE) - free_outp -= OUT_BUFFER_SIZE; - if (free_outp >= outp) - c = out_buffer + OUT_BUFFER_SIZE - free_outp; - else - c = outp - free_outp; - if (c > count) - c = count; + /* Check if resources are available */ + if (port->tr_running && + ((port->use_dma && port->active_tr_descr == port->catch_tr_descr) || + out_buf_count >= OUT_BUFFER_SIZE)) { + DEBUGWRITE(printk(KERN_DEBUG "sser%d full\n", dev)); + return -EAGAIN; + } + + buf_stop_ptr = port->out_buffer + OUT_BUFFER_SIZE; + + /* Determine pointer to the first free byte, before copying. */ + wr_ptr = rd_ptr + out_buf_count; + if (wr_ptr >= buf_stop_ptr) + wr_ptr -= OUT_BUFFER_SIZE; -// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); - if (copy_from_user((void*)free_outp, buf, c)) + /* If we wrap the ring buffer, let the user space program handle it by + * truncating the data. This could be more elegant, small buffer + * fragments may occur. + */ + bytes_free = OUT_BUFFER_SIZE - out_buf_count; + if (wr_ptr + bytes_free > buf_stop_ptr) + bytes_free = buf_stop_ptr - wr_ptr; + trunc_count = (count < bytes_free) ? count : bytes_free; + + if (copy_from_user(wr_ptr, buf, trunc_count)) return -EFAULT; - if (c != count) { - buf += c; - c1 = count - c; - DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1)); - if (copy_from_user((void*)out_buffer, buf, c1)) - return -EFAULT; - } - spin_lock_irqsave(&port->lock, flags); - port->out_count += count; - spin_unlock_irqrestore(&port->lock, flags); + DEBUGOUTBUF(printk(KERN_DEBUG "%-4d + %-4d = %-4d %p %p %p\n", + out_buf_count, trunc_count, + port->out_buf_count, port->out_buffer, + wr_ptr, buf_stop_ptr)); /* Make sure transmitter/receiver is running */ - if (!port->started) - { + if (!port->started) { reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); - reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); cfg.en = regk_sser_yes; - tr_cfg.tr_en = port->output; rec_cfg.rec_en = port->input; REG_WR(sser, port->regi_sser, rw_cfg, cfg); - REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); port->started = 1; } - if (file->f_flags & O_NONBLOCK) { - spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, (unsigned char* volatile )port->outp, c); - } - } - spin_unlock_irqrestore(&port->lock, flags); - DEBUGWRITE(printk("w d%d c %lu NB\n", - port->port_nbr, count)); - return count; + /* Setup wait if blocking */ + if (!(file->f_flags & O_NONBLOCK)) { + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); } - /* Sleep until all sent */ - - add_wait_queue(&port->out_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, port->outp, c); - } + port->out_buf_count += trunc_count; + if (port->use_dma) { + start_dma_out(port, wr_ptr, trunc_count); + } else if (!port->tr_running) { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + intr_mask.trdy = 1; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); } spin_unlock_irqrestore(&port->lock, flags); + + /* Exit if non blocking */ + if (file->f_flags & O_NONBLOCK) { + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu %08x\n", + port->port_nbr, trunc_count, + REG_RD_INT(dma, port->regi_dmaout, r_intr))); + return trunc_count; + } + schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) - { return -EINTR; - } - DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count)); - return count; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", + port->port_nbr, trunc_count)); + return trunc_count; } static ssize_t sync_serial_read(struct file * file, char * buf, @@ -926,7 +1089,7 @@ static ssize_t sync_serial_read(struct file * file, char * buf, unsigned char* end; unsigned long flags; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -949,7 +1112,6 @@ static ssize_t sync_serial_read(struct file * file, char * buf, port->started = 1; } - /* Calculate number of available bytes */ /* Save pointers to avoid that they are modified by interrupt */ spin_lock_irqsave(&port->lock, flags); @@ -958,16 +1120,14 @@ static ssize_t sync_serial_read(struct file * file, char * buf, spin_unlock_irqrestore(&port->lock, flags); while ((start == end) && !port->full) /* No data */ { + DEBUGREAD(printk(KERN_DEBUG "&")); if (file->f_flags & O_NONBLOCK) - { return -EAGAIN; - } interruptible_sleep_on(&port->in_wait_q); if (signal_pending(current)) - { return -EINTR; - } + spin_lock_irqsave(&port->lock, flags); start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ @@ -1004,83 +1164,105 @@ static void send_word(sync_port* port) switch(tr_cfg.sample_size) { case 8: - port->out_count--; - tr_data.data = *port->outp++; + port->out_buf_count--; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 12: { - int data = (*port->outp++) << 8; - data |= *port->outp++; - port->out_count-=2; + int data = (*port->out_rd_ptr++) << 8; + data |= *port->out_rd_ptr++; + port->out_buf_count -= 2; tr_data.data = data; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; } break; case 16: - port->out_count-=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 24: - port->out_count-=3; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 3; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *port->outp++; + port->out_rd_ptr += 2; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 32: - port->out_count-=4; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 4; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_rd_ptr += 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; } } - -static void start_dma(struct sync_port* port, const char* data, int count) +static void start_dma_out(struct sync_port *port, + const char *data, int count) { - port->tr_running = 1; - port->out_descr.buf = (char*)virt_to_phys((char*)data); - port->out_descr.after = port->out_descr.buf + count; - port->out_descr.eol = port->out_descr.intr = 1; + port->active_tr_descr->buf = (char *) virt_to_phys((char *) data); + port->active_tr_descr->after = port->active_tr_descr->buf + count; + port->active_tr_descr->intr = 1; + + port->active_tr_descr->eol = 1; + port->prev_tr_descr->eol = 0; + + DEBUGTRDMA(printk(KERN_DEBUG "Inserting eolr:%p eol@:%p\n", + port->prev_tr_descr, port->active_tr_descr)); + port->prev_tr_descr = port->active_tr_descr; + port->active_tr_descr = phys_to_virt((int) port->active_tr_descr->next); + + if (!port->tr_running) { + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, + rw_tr_cfg); - port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr); - port->out_context.saved_data_buf = port->out_descr.buf; + port->out_context.next = 0; + port->out_context.saved_data = + (dma_descr_data *)virt_to_phys(port->prev_tr_descr); + port->out_context.saved_data_buf = port->prev_tr_descr->buf; + + DMA_START_CONTEXT(port->regi_dmaout, + virt_to_phys((char *)&port->out_context)); + + tr_cfg.tr_en = regk_sser_yes; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + DEBUGTRDMA(printk(KERN_DEBUG "dma s\n");); + } else { + DMA_CONTINUE_DATA(port->regi_dmaout); + DEBUGTRDMA(printk(KERN_DEBUG "dma c\n");); + } - DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context)); - DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); + port->tr_running = 1; } -static void start_dma_in(sync_port* port) +static void start_dma_in(sync_port *port) { int i; - char* buf; + char *buf; port->writep = port->flip; - if (port->writep > port->flip + port->in_buffer_size) - { + if (port->writep > port->flip + port->in_buffer_size) { panic("Offset too large in sync serial driver\n"); return; } buf = (char*)virt_to_phys(port->in_buffer); - for (i = 0; i < NUM_IN_DESCR; i++) { + for (i = 0; i < NBR_IN_DESCR; i++) { port->in_descr[i].buf = buf; port->in_descr[i].after = buf + port->inbufchunk; port->in_descr[i].intr = 1; @@ -1092,59 +1274,126 @@ static void start_dma_in(sync_port* port) port->in_descr[i-1].next = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_descr[i-1].eol = regk_sser_yes; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR - 1]; port->in_context.saved_data = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_context.saved_data_buf = port->in_descr[0].buf; DMA_START_CONTEXT(port->regi_dmain, virt_to_phys(&port->in_context)); } #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t tr_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; + reg_dma_rw_stat stat; int i; - struct dma_descr_data *descr; - unsigned int sentl; int found = 0; + int stop_sser = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) - { + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; - if (!port->enabled || !port->use_dma ) + if (!port->enabled || !port->use_dma) continue; + /* IRQ active for the port? */ masked = REG_RD(dma, port->regi_dmaout, r_masked_intr); + if (!masked.data) + continue; - if (masked.data) /* IRQ active for the port? */ - { - found = 1; - /* Clear IRQ */ - REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); - descr = &port->out_descr; - sentl = descr->after - descr->buf; - port->out_count -= sentl; - port->outp += sentl; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; - if (port->out_count) { - int c; - c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; - if (c > port->out_count) - c = port->out_count; - DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); - start_dma(port, port->outp, c); - } else { - DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); - port->tr_running = 0; + found = 1; + + /* Check if we should stop the DMA transfer */ + stat = REG_RD(dma, port->regi_dmaout, rw_stat); + if (stat.list_state == regk_dma_data_at_eol) + stop_sser = 1; + + /* Clear IRQ */ + REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); + + if (!stop_sser) { + /* The DMA has completed a descriptor, EOL was not + * encountered, so step relevant descriptor and + * datapointers forward. */ + int sent; + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGTXINT(printk(KERN_DEBUG "%-4d - %-4d = %-4d\t" + "in descr %p (ac: %p)\n", + port->out_buf_count, sent, + port->out_buf_count - sent, + port->catch_tr_descr, + port->active_tr_descr);); + port->out_buf_count -= sent; + port->catch_tr_descr = + phys_to_virt((int) port->catch_tr_descr->next); + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->buf); + } else { + int i, sent; + /* EOL handler. + * Note that if an EOL was encountered during the irq + * locked section of sync_ser_write the DMA will be + * restarted and the eol flag will be cleared. + * The remaining descriptors will be traversed by + * the descriptor interrupts as usual. + */ + i = 0; + while (!port->catch_tr_descr->eol) { + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "traversing descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + port->out_buf_count -= sent; + port->catch_tr_descr = phys_to_virt( + (int)port->catch_tr_descr->next); + i++; + if (i >= NBR_OUT_DESCR) { + /* TODO: Reset and recover */ + panic("sync_serial: missing eol"); + } } - wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "eol at descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + + port->out_buf_count -= sent; + + /* Update read pointer to first free byte, we + * may already be writing data there. */ + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->after); + if (port->out_rd_ptr > port->out_buffer + + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; + + reg_sser_rw_tr_cfg tr_cfg = + REG_RD(sser, port->regi_sser, rw_tr_cfg); + DEBUGTXINT(printk(KERN_DEBUG + "tr_int DMA stop %d, set catch @ %p\n", + port->out_buf_count, + port->active_tr_descr)); + if (port->out_buf_count != 0) + printk(KERN_CRIT "sync_ser: buffer not " + "empty after eol.\n"); + port->catch_tr_descr = port->active_tr_descr; + port->tr_running = 0; + tr_cfg.tr_en = regk_sser_no; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); } return IRQ_RETVAL(found); } /* tr_interrupt */ -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t rx_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; @@ -1152,7 +1401,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) int i; int found = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; @@ -1166,7 +1415,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) found = 1; while (REG_RD(dma, port->regi_dmain, rw_data) != virt_to_phys(port->next_rx_desc)) { - + DEBUGRXINT(printk(KERN_DEBUG "!")); if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { int first_size = port->flip + port->in_buffer_size - port->writep; memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size); @@ -1185,11 +1434,16 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) port->full = 1; } - port->next_rx_desc->eol = 0; - port->prev_rx_desc->eol = 1; - port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + port->next_rx_desc->eol = 1; + port->prev_rx_desc->eol = 0; + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 0); + port->prev_rx_desc = port->next_rx_desc; port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); - wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 1); + /* wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); DMA_CONTINUE(port->regi_dmain); REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr); @@ -1201,15 +1455,15 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) #endif /* SYNC_SER_DMA */ #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t manual_interrupt(int irq, void *dev_id) { int i; int found = 0; reg_sser_r_masked_intr masked; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { - sync_port* port = &ports[i]; + sync_port *port = &ports[i]; if (!port->enabled || port->use_dma) { @@ -1263,7 +1517,7 @@ static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs if (masked.trdy) /* Transmitter ready? */ { found = 1; - if (port->out_count > 0) /* More data to send */ + if (port->out_buf_count > 0) /* More data to send */ send_word(port); else /* transmission finished */ { diff --git a/arch/cris/arch-v32/kernel/Makefile b/arch/cris/arch-v32/kernel/Makefile index 5d5b613cde8..993d987b007 100644 --- a/arch/cris/arch-v32/kernel/Makefile +++ b/arch/cris/arch-v32/kernel/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $ # # Makefile for the linux kernel. # @@ -6,9 +5,9 @@ extra-y := head.o -obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \ +obj-y := entry.o traps.o irq.o debugport.o \ process.o ptrace.o setup.o signal.o traps.o time.o \ - arbiter.o io.o + cache.o cacheflush.o obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o diff --git a/arch/cris/arch-v32/kernel/arbiter.c b/arch/cris/arch-v32/kernel/arbiter.c deleted file mode 100644 index 420a5312ed0..00000000000 --- a/arch/cris/arch-v32/kernel/arbiter.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Memory arbiter functions. Allocates bandwidth through the - * arbiter and sets up arbiter breakpoints. - * - * The algorithm first assigns slots to the clients that has specified - * bandwidth (e.g. ethernet) and then the remaining slots are divided - * on all the active clients. - * - * Copyright (c) 2004, 2005 Axis Communications AB. - */ - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/arbiter.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <linux/interrupt.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/spinlock.h> -#include <asm/io.h> - -struct crisv32_watch_entry -{ - unsigned long instance; - watch_callback* cb; - unsigned long start; - unsigned long end; - int used; -}; - -#define NUMBER_OF_BP 4 -#define NBR_OF_CLIENTS 14 -#define NBR_OF_SLOTS 64 -#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ -#define INTMEM_BANDWIDTH 400000000 -#define NBR_OF_REGIONS 2 - -static struct crisv32_watch_entry watches[NUMBER_OF_BP] = -{ - {regi_marb_bp0}, - {regi_marb_bp1}, - {regi_marb_bp2}, - {regi_marb_bp3} -}; - -static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; - -DEFINE_SPINLOCK(arbiter_lock); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); - -static void crisv32_arbiter_config(int region) -{ - int slot; - int client; - int interval = 0; - int val[NBR_OF_SLOTS]; - - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - val[slot] = NBR_OF_CLIENTS + 1; - - for (client = 0; client < NBR_OF_CLIENTS; client++) - { - int pos; - if (!requested_slots[region][client]) - continue; - interval = NBR_OF_SLOTS / requested_slots[region][client]; - pos = 0; - while (pos < NBR_OF_SLOTS) - { - if (val[pos] != NBR_OF_CLIENTS + 1) - pos++; - else - { - val[pos] = client; - pos += interval; - } - } - } - - client = 0; - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - { - if (val[slot] == NBR_OF_CLIENTS + 1) - { - int first = client; - while(!active_clients[region][client]) { - client = (client + 1) % NBR_OF_CLIENTS; - if (client == first) - break; - } - val[slot] = client; - client = (client + 1) % NBR_OF_CLIENTS; - } - if (region == EXT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); - else if (region == INT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); - } -} - -extern char _stext, _etext; - -static void crisv32_arbiter_init(void) -{ - static int initialized = 0; - - if (initialized) - return; - - initialized = 1; - - /* CPU caches are active. */ - active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; - crisv32_arbiter_config(EXT_REGION); - crisv32_arbiter_config(INT_REGION); - - if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, - "arbiter", NULL)) - printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); - -#ifndef CONFIG_ETRAX_KGDB - /* Global watch for writes to kernel text segment. */ - crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, - arbiter_all_clients, arbiter_all_write, NULL); -#endif -} - - - -int crisv32_arbiter_allocate_bandwidth(int client, int region, - unsigned long bandwidth) -{ - int i; - int total_assigned = 0; - int total_clients = 0; - int req; - - crisv32_arbiter_init(); - - for (i = 0; i < NBR_OF_CLIENTS; i++) - { - total_assigned += requested_slots[region][i]; - total_clients += active_clients[region][i]; - } - req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); - - if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) - return -ENOMEM; - - active_clients[region][client] = 1; - requested_slots[region][client] = req; - crisv32_arbiter_config(region); - - return 0; -} - -int crisv32_arbiter_watch(unsigned long start, unsigned long size, - unsigned long clients, unsigned long accesses, - watch_callback* cb) -{ - int i; - - crisv32_arbiter_init(); - - if (start > 0x80000000) { - printk("Arbiter: %lX doesn't look like a physical address", start); - return -EFAULT; - } - - spin_lock(&arbiter_lock); - - for (i = 0; i < NUMBER_OF_BP; i++) { - if (!watches[i].used) { - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - watches[i].used = 1; - watches[i].start = start; - watches[i].end = start + size; - watches[i].cb = cb; - - REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start); - REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end); - REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses); - REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients); - - if (i == 0) - intr_mask.bp0 = regk_marb_yes; - else if (i == 1) - intr_mask.bp1 = regk_marb_yes; - else if (i == 2) - intr_mask.bp2 = regk_marb_yes; - else if (i == 3) - intr_mask.bp3 = regk_marb_yes; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - spin_unlock(&arbiter_lock); - - return i; - } - } - spin_unlock(&arbiter_lock); - return -ENOMEM; -} - -int crisv32_arbiter_unwatch(int id) -{ - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - crisv32_arbiter_init(); - - spin_lock(&arbiter_lock); - - if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { - spin_unlock(&arbiter_lock); - return -EINVAL; - } - - memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); - - if (id == 0) - intr_mask.bp0 = regk_marb_no; - else if (id == 1) - intr_mask.bp2 = regk_marb_no; - else if (id == 2) - intr_mask.bp2 = regk_marb_no; - else if (id == 3) - intr_mask.bp3 = regk_marb_no; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - - spin_unlock(&arbiter_lock); - return 0; -} - -extern void show_registers(struct pt_regs *regs); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) -{ - reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); - reg_marb_bp_r_brk_clients r_clients; - reg_marb_bp_r_brk_addr r_addr; - reg_marb_bp_r_brk_op r_op; - reg_marb_bp_r_brk_first_client r_first; - reg_marb_bp_r_brk_size r_size; - reg_marb_bp_rw_ack ack = {0}; - reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; - struct crisv32_watch_entry* watch; - - if (masked_intr.bp0) { - watch = &watches[0]; - ack_intr.bp0 = regk_marb_yes; - } else if (masked_intr.bp1) { - watch = &watches[1]; - ack_intr.bp1 = regk_marb_yes; - } else if (masked_intr.bp2) { - watch = &watches[2]; - ack_intr.bp2 = regk_marb_yes; - } else if (masked_intr.bp3) { - watch = &watches[3]; - ack_intr.bp3 = regk_marb_yes; - } else { - return IRQ_NONE; - } - - /* Retrieve all useful information and print it. */ - r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); - r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); - r_op = REG_RD(marb_bp, watch->instance, r_brk_op); - r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); - r_size = REG_RD(marb_bp, watch->instance, r_brk_size); - - printk("Arbiter IRQ\n"); - printk("Clients %X addr %X op %X first %X size %X\n", - REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); - - REG_WR(marb_bp, watch->instance, rw_ack, ack); - REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); - - printk("IRQ occured at %lX\n", regs->erp); - - if (watch->cb) - watch->cb(); - - - return IRQ_HANDLED; -} diff --git a/arch/cris/arch-v32/kernel/crisksyms.c b/arch/cris/arch-v32/kernel/crisksyms.c index e513da71124..77d02c15a7f 100644 --- a/arch/cris/arch-v32/kernel/crisksyms.c +++ b/arch/cris/arch-v32/kernel/crisksyms.c @@ -2,7 +2,8 @@ #include <linux/irq.h> #include <asm/arch/dma.h> #include <asm/arch/intmem.h> -#include <asm/arch/pinmux.h> +#include <asm/arch/mach/pinmux.h> +#include <asm/arch/io.h> /* Functions for allocating DMA channels */ EXPORT_SYMBOL(crisv32_request_dma); @@ -16,7 +17,11 @@ EXPORT_SYMBOL(crisv32_intmem_virt_to_phys); /* Functions for handling pinmux */ EXPORT_SYMBOL(crisv32_pinmux_alloc); +EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed); EXPORT_SYMBOL(crisv32_pinmux_dealloc); +EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed); +EXPORT_SYMBOL(crisv32_io_get_name); +EXPORT_SYMBOL(crisv32_io_get); /* Functions masking/unmasking interrupts */ EXPORT_SYMBOL(mask_irq); diff --git a/arch/cris/arch-v32/kernel/debugport.c b/arch/cris/arch-v32/kernel/debugport.c index d1272ad9215..15af4c29315 100644 --- a/arch/cris/arch-v32/kernel/debugport.c +++ b/arch/cris/arch-v32/kernel/debugport.c @@ -4,17 +4,12 @@ #include <linux/console.h> #include <linux/init.h> -#include <linux/major.h> -#include <linux/delay.h> -#include <linux/tty.h> #include <asm/system.h> -#include <asm/io.h> -#include <asm/arch/hwregs/ser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/pinmux.h> - -#include <asm/irq.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/dma_defs.h> +#include <asm/arch/mach/pinmux.h> struct dbg_port { @@ -59,45 +54,50 @@ struct dbg_port ports[] = 115200, 'N', 8 - } + }, +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + { + 4, + regi_ser4, + 0, + 115200, + 'N', + 8 + }, +#endif }; static struct dbg_port *port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_DEBUG_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_DEBUG_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_DEBUG_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #ifdef CONFIG_ETRAX_KGDB static struct dbg_port *kgdb_port = #if defined(CONFIG_ETRAX_KGDB_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_KGDB_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_KGDB_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_KGDB_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_KGDB_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #endif -#ifdef CONFIG_ETRAXFS_SIM -extern void print_str( const char *str ); -static char buffer[1024]; -static char msg[] = "Debug: "; -static int buffer_pos = sizeof(msg) - 1; -#endif - -extern struct tty_driver *serial_driver; - static void start_port(struct dbg_port* p) { @@ -114,6 +114,10 @@ start_port(struct dbg_port* p) crisv32_pinmux_alloc_fixed(pinmux_ser2); else if (p->nbr == 3) crisv32_pinmux_alloc_fixed(pinmux_ser3); +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + else if (p->nbr == 4) + crisv32_pinmux_alloc_fixed(pinmux_ser4); +#endif /* Set up serial port registers */ reg_ser_rw_tr_ctrl tr_ctrl = {0}; @@ -156,124 +160,21 @@ start_port(struct dbg_port* p) REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); } -/* No debug */ -#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - return; -} - -/* Target debug */ -#elif !defined(CONFIG_ETRAXFS_SIM) - -static void -console_write_direct(struct console *co, const char *buf, unsigned int len) -{ - int i; - reg_ser_r_stat_din stat; - reg_ser_rw_tr_dma_en tr_dma_en, old; - - /* Switch to manual mode */ - tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); - if (tr_dma_en.en == regk_ser_yes) { - tr_dma_en.en = regk_ser_no; - REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); - } - - /* Send data */ - for (i = 0; i < len; i++) { - /* LF -> CRLF */ - if (buf[i] == '\n') { - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, '\r'); - } - /* Wait until transmitter is ready and send.*/ - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, buf[i]); - } - - /* Restore mode */ - if (tr_dma_en.en != old.en) - REG_WR(ser, port->instance, rw_tr_dma_en, old); -} - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - if (!port) - return; - console_write_direct(co, buf, len); -} - - - -#else - -/* VCS debug */ - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - char* pos; - pos = memchr(buf, '\n', len); - if (pos) { - int l = ++pos - buf; - memcpy(buffer + buffer_pos, buf, l); - memcpy(buffer, msg, sizeof(msg) - 1); - buffer[buffer_pos + l] = '\0'; - print_str(buffer); - buffer_pos = sizeof(msg) - 1; - if (pos - buf != len) { - memcpy(buffer + buffer_pos, pos, len - l); - buffer_pos += len - l; - } - } else { - memcpy(buffer + buffer_pos, buf, len); - buffer_pos += len; - } -} - -#endif - -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write(NULL, buf, strlen(buf)); - return printed_len; -} - -void -stupid_debug(char* buf) -{ - console_write(NULL, buf, strlen(buf)); -} - #ifdef CONFIG_ETRAX_KGDB /* Use polling to get a single character from the kernel debug port */ int getDebugChar(void) { - reg_ser_rs_status_data stat; + reg_ser_rs_stat_din stat; reg_ser_rw_ack_intr ack_intr = { 0 }; do { - stat = REG_RD(ser, kgdb_instance, rs_status_data); - } while (!stat.data_avail); + stat = REG_RD(ser, kgdb_port->instance, rs_stat_din); + } while (!stat.dav); /* Ack the data_avail interrupt. */ - ack_intr.data_avail = 1; - REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); + ack_intr.dav = 1; + REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr); return stat.data; } @@ -282,173 +183,18 @@ getDebugChar(void) void putDebugChar(int val) { - reg_ser_r_status_data stat; + reg_ser_r_stat_din stat; do { - stat = REG_RD (ser, kgdb_instance, r_status_data); - } while (!stat.tr_ready); - REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); + stat = REG_RD(ser, kgdb_port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, kgdb_port->instance, rw_dout, val); } #endif /* CONFIG_ETRAX_KGDB */ -static int __init -console_setup(struct console *co, char *options) -{ - char* s; - - if (options) { - port = &ports[co->index]; - port->baudrate = 115200; - port->parity = 'N'; - port->bits = 8; - port->baudrate = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) port->parity = *s++; - if (*s) port->bits = *s++ - '0'; - port->started = 0; - start_port(port); - } - return 0; -} - -/* This is a dummy serial device that throws away anything written to it. - * This is used when no debug output is wanted. - */ -static struct tty_driver dummy_driver; - -static int dummy_open(struct tty_struct *tty, struct file * filp) -{ - return 0; -} - -static void dummy_close(struct tty_struct *tty, struct file * filp) -{ -} - -static int dummy_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - return count; -} - -static int -dummy_write_room(struct tty_struct *tty) -{ - return 8192; -} - -void __init -init_dummy_console(void) -{ - memset(&dummy_driver, 0, sizeof(struct tty_driver)); - dummy_driver.driver_name = "serial"; - dummy_driver.name = "ttyS"; - dummy_driver.major = TTY_MAJOR; - dummy_driver.minor_start = 68; - dummy_driver.num = 1; /* etrax100 has 4 serial ports */ - dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; - dummy_driver.subtype = SERIAL_TYPE_NORMAL; - dummy_driver.init_termios = tty_std_termios; - dummy_driver.init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ - dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - - dummy_driver.open = dummy_open; - dummy_driver.close = dummy_close; - dummy_driver.write = dummy_write; - dummy_driver.write_room = dummy_write_room; - if (tty_register_driver(&dummy_driver)) - panic("Couldn't register dummy serial driver\n"); -} - -static struct tty_driver* -crisv32_console_device(struct console* co, int *index) -{ - if (port) - *index = port->nbr; - return port ? serial_driver : &dummy_driver; -} - -static struct console sercons = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : -1, - cflag : 0, - next : NULL -}; -static struct console sercons0 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 0, - cflag : 0, - next : NULL -}; - -static struct console sercons1 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 1, - cflag : 0, - next : NULL -}; -static struct console sercons2 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 2, - cflag : 0, - next : NULL -}; -static struct console sercons3 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 3, - cflag : 0, - next : NULL -}; - /* Register console for printk's, etc. */ int __init init_etrax_debug(void) { - static int first = 1; - - if (!first) { - unregister_console(&sercons); - register_console(&sercons0); - register_console(&sercons1); - register_console(&sercons2); - register_console(&sercons3); - init_dummy_console(); - return 0; - } - first = 0; - register_console(&sercons); start_port(port); #ifdef CONFIG_ETRAX_KGDB @@ -456,5 +202,3 @@ init_etrax_debug(void) #endif /* CONFIG_ETRAX_KGDB */ return 0; } - -__initcall(init_etrax_debug); diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S index f9d27807b91..eebbaba4543 100644 --- a/arch/cris/arch-v32/kernel/entry.S +++ b/arch/cris/arch-v32/kernel/entry.S @@ -10,7 +10,7 @@ * after a timer-interrupt and after each system call. * * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. + * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in fork.c:copy_process, signal.c:do_signal, * ptrace.c and ptrace.h @@ -281,12 +281,10 @@ _work_notifysig: ;; Deal with pending signals and notify-resume requests. addoq +TI_flags, $r0, $acr - move.d [$acr], $r13 ; The thread_info_flags parameter. - move.d $r9, $r10 ; do_notify_resume syscall/irq param. - moveq 0, $r11 ; oldset param - 0 in this case. - move.d $sp, $r12 ; The regs param. + move.d [$acr], $r12 ; The thread_info_flags parameter. + move.d $sp, $r11 ; The regs param. jsr do_notify_resume - nop + move.d $r9, $r10 ; do_notify_resume syscall/irq param. ba _Rexit nop @@ -396,7 +394,7 @@ nmi_interrupt: btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0 bpl 1f nop - jsr handle_watchdog_bite ; In time.c. + jsr handle_watchdog_bite ; In time.c. move.d $sp, $r10 ; Pointer to registers 1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0 bpl 1f @@ -515,6 +513,13 @@ _ugdb_handle_exception: ba do_sigtrap ; SIGTRAP the offending process. move.d [$sp+], $r0 ; Restore R0 in delay slot. + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop + .data .section .rodata,"a" @@ -778,21 +783,21 @@ sys_call_table: .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait .long sys_remap_file_pages - .long sys_set_tid_address - .long sys_timer_create - .long sys_timer_settime /* 260 */ - .long sys_timer_gettime - .long sys_timer_getoverrun - .long sys_timer_delete - .long sys_clock_settime - .long sys_clock_gettime /* 265 */ - .long sys_clock_getres - .long sys_clock_nanosleep + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep .long sys_statfs64 .long sys_fstatfs64 .long sys_tgkill /* 270 */ .long sys_utimes - .long sys_fadvise64_64 + .long sys_fadvise64_64 .long sys_ni_syscall /* sys_vserver */ .long sys_ni_syscall /* sys_mbind */ .long sys_ni_syscall /* 275 sys_get_mempolicy */ @@ -805,6 +810,48 @@ sys_call_table: .long sys_mq_getsetattr .long sys_ni_syscall /* reserved for kexec */ .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + .long sys_utimensat /* 320 */ + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime /* * NOTE!! This doesn't have to be exact - we just have diff --git a/arch/cris/arch-v32/kernel/fasttimer.c b/arch/cris/arch-v32/kernel/fasttimer.c index b40551f9f40..2de9d5849ef 100644 --- a/arch/cris/arch-v32/kernel/fasttimer.c +++ b/arch/cris/arch-v32/kernel/fasttimer.c @@ -1,110 +1,9 @@ -/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $ +/* * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX FS - * This may be useful in other OS than Linux so use 2 space indentation... - * - * $Log: fasttimer.c,v $ - * Revision 1.11 2005/01/04 11:15:46 starvik - * Don't share timer IRQ. - * - * Revision 1.10 2004/12/07 09:19:38 starvik - * Corrected includes. - * Use correct interrupt macros. - * - * Revision 1.9 2004/05/14 10:18:58 starvik - * Export fast_timer_list - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.7 2003/07/10 12:06:14 starvik - * Return IRQ_NONE if irq wasn't handled - * - * Revision 1.6 2003/07/04 08:27:49 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/05 10:16:22 johana - * New INTR_VECT macros. - * - * Revision 1.4 2003/06/03 08:49:45 johana - * Fixed typo. - * - * Revision 1.3 2003/06/02 12:51:27 johana - * Now compiles. - * Commented some include files that probably can be removed. - * - * Revision 1.2 2003/06/02 12:09:41 johana - * Ported to ETRAX FS using the trig interrupt instead of timer1. - * - * Revision 1.3 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.2 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.1 2002/11/18 07:58:06 starvik - * Fast timers (from Linux 2.4) - * - * Revision 1.5 2002/10/15 06:21:39 starvik - * Added call to init_waitqueue_head * - * Revision 1.4 2002/05/28 17:47:59 johana - * Added del_fast_timer() - * - * Revision 1.3 2002/05/28 16:16:07 johana - * Handle empty fast_timer_list - * - * Revision 1.2 2002/05/27 15:38:42 johana - * Made it compile without warnings on Linux 2.4. - * (includes, wait_queue, PROC_FS and snprintf) - * - * Revision 1.1 2002/05/27 15:32:25 johana - * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. - * - * Revision 1.8 2001/11/27 13:50:40 pkj - * Disable interrupts while stopping the timer and while modifying the - * list of active timers in timer1_handler() as it may be interrupted - * by other interrupts (e.g., the serial interrupt) which may add fast - * timers. - * - * Revision 1.7 2001/11/22 11:50:32 pkj - * * Only store information about the last 16 timers. - * * proc_fasttimer_read() now uses an allocated buffer, since it - * requires more space than just a page even for only writing the - * last 16 timers. The buffer is only allocated on request, so - * unless /proc/fasttimer is read, it is never allocated. - * * Renamed fast_timer_started to fast_timers_started to match - * fast_timers_added and fast_timers_expired. - * * Some clean-up. - * - * Revision 1.6 2000/12/13 14:02:08 johana - * Removed volatile for fast_timer_list - * - * Revision 1.5 2000/12/13 13:55:35 johana - * Added DEBUG_LOG, added som cli() and cleanup - * - * Revision 1.4 2000/12/05 13:48:50 johana - * Added range check when writing proc file, modified timer int handling - * - * Revision 1.3 2000/11/23 10:10:20 johana - * More debug/logging possibilities. - * Moved GET_JIFFIES_USEC() to timex.h and time.c - * - * Revision 1.2 2000/11/01 13:41:04 johana - * Clean up and bugfixes. - * Created new do_gettimeofday_fast() that gets a timeval struct - * with time based on jiffies and *R_TIMER0_DATA, uses a table - * for fast conversion of timer value to microseconds. - * (Much faster the standard do_gettimeofday() and we don't really - * want to use the true time - we want the "uptime" so timers don't screw up - * when we change the time. - * TODO: Add efficient support for continuous timers as well. - * - * Revision 1.1 2000/10/26 15:49:16 johana - * Added fasttimer, highresolution timers. - * - * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden */ #include <linux/errno.h> @@ -122,9 +21,9 @@ #include <linux/version.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> #include <asm/fasttimer.h> #include <linux/proc_fs.h> @@ -140,30 +39,25 @@ #define DEBUG_LOG_INCLUDED #define FAST_TIMER_LOG -//#define FAST_TIMER_TEST +/* #define FAST_TIMER_TEST */ #define FAST_TIMER_SANITY_CHECKS #ifdef FAST_TIMER_SANITY_CHECKS -#define SANITYCHECK(x) x -static int sanity_failed = 0; -#else -#define SANITYCHECK(x) +static int sanity_failed; #endif #define D1(x) #define D2(x) #define DP(x) -#define __INLINE__ inline - -static int fast_timer_running = 0; -static int fast_timers_added = 0; -static int fast_timers_started = 0; -static int fast_timers_expired = 0; -static int fast_timers_deleted = 0; -static int fast_timer_is_init = 0; -static int fast_timer_ints = 0; +static unsigned int fast_timer_running; +static unsigned int fast_timers_added; +static unsigned int fast_timers_started; +static unsigned int fast_timers_expired; +static unsigned int fast_timers_deleted; +static unsigned int fast_timer_is_init; +static unsigned int fast_timer_ints; struct fast_timer *fast_timer_list = NULL; @@ -171,8 +65,8 @@ struct fast_timer *fast_timer_list = NULL; #define DEBUG_LOG_MAX 128 static const char * debug_log_string[DEBUG_LOG_MAX]; static unsigned long debug_log_value[DEBUG_LOG_MAX]; -static int debug_log_cnt = 0; -static int debug_log_cnt_wrapped = 0; +static unsigned int debug_log_cnt; +static unsigned int debug_log_cnt_wrapped; #define DEBUG_LOG(string, value) \ { \ @@ -202,103 +96,92 @@ struct fast_timer timer_expired_log[NUM_TIMER_STATS]; int timer_div_settings[NUM_TIMER_STATS]; int timer_delay_settings[NUM_TIMER_STATS]; +struct work_struct fast_work; static void -timer_trig_handler(void); +timer_trig_handler(struct work_struct *work); /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ -void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +inline void do_gettimeofday_fast(struct fasttime_t *tv) { - unsigned long sec = jiffies; - unsigned long usec = GET_JIFFIES_USEC(); - - usec += (sec % HZ) * (1000000 / HZ); - sec = sec / HZ; - - if (usec > 1000000) - { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_jiff = jiffies; + tv->tv_usec = GET_JIFFIES_USEC(); } -int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1) { - if (t0->tv_sec < t1->tv_sec) - { - return -1; - } - else if (t0->tv_sec > t1->tv_sec) - { - return 1; - } - if (t0->tv_usec < t1->tv_usec) - { - return -1; - } - else if (t0->tv_usec > t1->tv_usec) - { - return 1; - } - return 0; + /* Compare jiffies. Takes care of wrapping */ + if (time_before(t0->tv_jiff, t1->tv_jiff)) + return -1; + else if (time_after(t0->tv_jiff, t1->tv_jiff)) + return 1; + + /* Compare us */ + if (t0->tv_usec < t1->tv_usec) + return -1; + else if (t0->tv_usec > t1->tv_usec) + return 1; + return 0; } /* Called with ints off */ -void __INLINE__ start_timer_trig(unsigned long delay_us) +inline void start_timer_trig(unsigned long delay_us) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; reg_timer_rw_trig trig; reg_timer_rw_trig_cfg trig_cfg = { 0 }; - reg_timer_r_time r_time; + reg_timer_r_time r_time0; + reg_timer_r_time r_time1; + unsigned char trig_wrap; + unsigned char time_wrap; - r_time = REG_RD(timer, regi_timer, r_time); + r_time0 = REG_RD(timer, regi_timer0, r_time); D1(printk("start_timer_trig : %d us freq: %i div: %i\n", delay_us, freq_index, div)); /* Clear trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); - /* Set timer values */ - /* r_time is 100MHz (10 ns resolution) */ - trig = r_time + delay_us*(1000/10); + /* Set timer values and check if trigger wraps. */ + /* r_time is 100MHz (10 ns resolution) */ + trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0; timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig; timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); /* Start timer */ - REG_WR(timer, regi_timer, rw_trig, trig); + REG_WR(timer, regi_timer0, rw_trig, trig); trig_cfg.tmr = regk_timer_time; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Check if we have already passed the trig time */ - r_time = REG_RD(timer, regi_timer, r_time); - if (r_time < trig) { + r_time1 = REG_RD(timer, regi_timer0, r_time); + time_wrap = r_time1 < r_time0; + + if ((trig_wrap && !time_wrap) || (r_time1 < trig)) { /* No, Enable trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 1; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); fast_timers_started++; fast_timer_running = 1; - } - else - { + } else { /* We have passed the time, disable trig point, ack intr */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); - /* call the int routine directly */ - timer_trig_handler(); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); + /* call the int routine */ + INIT_WORK(&fast_work, timer_trig_handler); + schedule_work(&fast_work); } } @@ -320,22 +203,20 @@ void start_one_shot_timer(struct fast_timer *t, do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; - SANITYCHECK({ /* Check so this is not in the list already... */ - while (tmp != NULL) - { - if (tmp == t) - { - printk("timer name: %s data: 0x%08lX already in list!\n", name, data); - sanity_failed++; - return; - } - else - { - tmp = tmp->next; - } - } - tmp = fast_timer_list; - }); +#ifdef FAST_TIMER_SANITY_CHECKS + /* Check so this is not in the list already... */ + while (tmp != NULL) { + if (tmp == t) { + printk(KERN_DEBUG + "timer name: %s data: 0x%08lX already " + "in list!\n", name, data); + sanity_failed++; + goto done; + } else + tmp = tmp->next; + } + tmp = fast_timer_list; +#endif t->delay_us = delay_us; t->function = function; @@ -343,11 +224,10 @@ void start_one_shot_timer(struct fast_timer *t, t->name = name; t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; - t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; - if (t->tv_expires.tv_usec > 1000000) - { + t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ; + if (t->tv_expires.tv_usec > 1000000) { t->tv_expires.tv_usec -= 1000000; - t->tv_expires.tv_sec++; + t->tv_expires.tv_jiff += HZ; } #ifdef FAST_TIMER_LOG timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; @@ -355,15 +235,12 @@ void start_one_shot_timer(struct fast_timer *t, fast_timers_added++; /* Check if this should timeout before anything else */ - if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) - { + if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0) { /* Put first in list and modify the timer value */ t->prev = NULL; t->next = fast_timer_list; if (fast_timer_list) - { fast_timer_list->prev = t; - } fast_timer_list = t; #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; @@ -372,10 +249,8 @@ void start_one_shot_timer(struct fast_timer *t, } else { /* Put in correct place in list */ while (tmp->next && - timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) - { + fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) tmp = tmp->next; - } /* Insert t after tmp */ t->prev = tmp; t->next = tmp->next; @@ -388,6 +263,7 @@ void start_one_shot_timer(struct fast_timer *t, D2(printk("start_one_shot_timer: %d us done\n", delay_us)); +done: local_irq_restore(flags); } /* start_one_shot_timer */ @@ -431,19 +307,18 @@ int del_fast_timer(struct fast_timer * t) /* Timer interrupt handler for trig interrupts */ static irqreturn_t -timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_trig_interrupt(int irq, void *dev_id) { reg_timer_r_masked_intr masked_intr; - /* Check if the timer interrupt is for us (a trig int) */ - masked_intr = REG_RD(timer, regi_timer, r_masked_intr); + masked_intr = REG_RD(timer, regi_timer0, r_masked_intr); if (!masked_intr.trig) return IRQ_NONE; - timer_trig_handler(); + timer_trig_handler(NULL); return IRQ_HANDLED; } -static void timer_trig_handler(void) +static void timer_trig_handler(struct work_struct *work) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; @@ -451,38 +326,45 @@ static void timer_trig_handler(void) struct fast_timer *t; unsigned long flags; + /* We keep interrupts disabled not only when we modify the + * fast timer list, but any time we hold a reference to a + * timer in the list, since del_fast_timer may be called + * from (another) interrupt context. Thus, the only time + * when interrupts are enabled is when calling the timer + * callback function. + */ local_irq_save(flags); /* Clear timer trig interrupt */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); /* First stop timer, then ack interrupt */ /* Stop timer */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); fast_timer_running = 0; fast_timer_ints++; - local_irq_restore(flags); + fast_timer_function_type *f; + unsigned long d; t = fast_timer_list; - while (t) - { - struct timeval tv; + while (t) { + struct fasttime_t tv; /* Has it really expired? */ do_gettimeofday_fast(&tv); - D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + D1(printk(KERN_DEBUG + "t: %is %06ius\n", tv.tv_jiff, tv.tv_usec)); - if (timeval_cmp(&t->tv_expires, &tv) <= 0) - { + if (fasttime_cmp(&t->tv_expires, &tv) <= 0) { /* Yes it has expired */ #ifdef FAST_TIMER_LOG timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; @@ -490,84 +372,77 @@ static void timer_trig_handler(void) fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - local_irq_save(flags); if (t->prev) - { t->prev->next = t->next; - } else - { fast_timer_list = t->next; - } if (t->next) - { t->next->prev = t->prev; - } t->prev = NULL; t->next = NULL; - local_irq_restore(flags); - if (t->function != NULL) - { - t->function(t->data); - } - else - { + /* Save function callback data before enabling + * interrupts, since the timer may be removed and we + * don't know how it was allocated (e.g. ->function + * and ->data may become overwritten after deletion + * if the timer was stack-allocated). + */ + f = t->function; + d = t->data; + + if (f != NULL) { + /* Run the callback function with interrupts + * enabled. */ + local_irq_restore(flags); + f(d); + local_irq_save(flags); + } else DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints); - } - } - else - { + } else { /* Timer is to early, let's set it again using the normal routines */ D1(printk(".\n")); } - local_irq_save(flags); - if ((t = fast_timer_list) != NULL) - { + t = fast_timer_list; + if (t != NULL) { /* Start next timer.. */ - long us; - struct timeval tv; + long us = 0; + struct fasttime_t tv; do_gettimeofday_fast(&tv); - us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + - t->tv_expires.tv_usec - tv.tv_usec); - if (us > 0) - { - if (!fast_timer_running) - { + + /* time_after_eq takes care of wrapping */ + if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff)) + us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * + 1000000 / HZ + t->tv_expires.tv_usec - + tv.tv_usec); + + if (us > 0) { + if (!fast_timer_running) { #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; #endif start_timer_trig(us); } - local_irq_restore(flags); break; - } - else - { + } else { /* Timer already expired, let's handle it better late than never. * The normal loop handles it */ D1(printk("e! %d\n", us)); } } - local_irq_restore(flags); } - if (!t) - { + local_irq_restore(flags); + + if (!t) D1(printk("ttrig stop!\n")); - } } static void wake_up_func(unsigned long data) { -#ifdef DECLARE_WAITQUEUE wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; -#else - struct wait_queue **sleep_wait_p = (struct wait_queue **)data; -#endif wake_up(sleep_wait_p); } @@ -577,28 +452,17 @@ static void wake_up_func(unsigned long data) void schedule_usleep(unsigned long us) { struct fast_timer t; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t sleep_wait; init_waitqueue_head(&sleep_wait); - { - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue *sleep_wait = NULL; - struct wait_queue wait = { current, NULL }; -#endif D1(printk("schedule_usleep(%d)\n", us)); - add_wait_queue(&sleep_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, "usleep"); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&sleep_wait, &wait); + /* Uninterruptible sleep on the fast timer. (The condition is + * somewhat redundant since the timer is what wakes us up.) */ + wait_event(sleep_wait, !fast_timer_pending(&t)); + D1(printk("done schedule_usleep(%d)\n", us)); -#ifdef DECLARE_WAITQUEUE - } -#endif } #ifdef CONFIG_PROC_FS @@ -618,20 +482,22 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len unsigned long flags; int i = 0; int num_to_show; - struct timeval tv; + struct fasttime_t tv; struct fast_timer *t, *nextt; static char *bigbuf = NULL; static unsigned long used; - if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) - { - used = 0; - bigbuf[0] = '\0'; - return 0; - } - - if (!offset || !used) - { + if (!bigbuf) { + bigbuf = vmalloc(BIG_BUF_SIZE); + if (!bigbuf) { + used = 0; + if (buf) + buf[0] = '\0'; + return 0; + } + } + + if (!offset || !used) { do_gettimeofday_fast(&tv); used = 0; @@ -648,7 +514,7 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Fast timer running: %s\n", fast_timer_running ? "yes" : "no"); used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", - (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_jiff, (unsigned long)tv.tv_usec); #ifdef FAST_TIMER_SANITY_CHECKS used += sprintf(bigbuf + used, "Sanity failed: %i\n", @@ -661,10 +527,8 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len int end_i = debug_log_cnt; i = 0; - if (debug_log_cnt_wrapped) - { + if (debug_log_cnt_wrapped) i = debug_log_cnt; - } while ((i != end_i || (debug_log_cnt_wrapped && !used)) && used+100 < BIG_BUF_SIZE) @@ -697,9 +561,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -719,9 +583,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -739,9 +603,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -752,26 +616,25 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Active timers:\n"); local_irq_save(flags); - local_irq_save(flags); t = fast_timer_list; while (t != NULL && (used+100 < BIG_BUF_SIZE)) { nextt = t->next; local_irq_restore(flags); used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " - "d: %6li us data: 0x%08lX" -/* " func: 0x%08lX" */ - "\n", - t->name, - (unsigned long)t->tv_set.tv_sec, - (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, - (unsigned long)t->tv_expires.tv_usec, + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_jiff, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_jiff, + (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data /* , t->function */ ); - local_irq_disable(); + local_irq_save(flags); if (t->next != nextt) { printk("timer removed!\n"); @@ -800,7 +663,7 @@ static volatile int num_test_timeout = 0; static struct fast_timer tr[10]; static int exp_num[10]; -static struct timeval tv_exp[100]; +static struct fasttime_t tv_exp[100]; static void test_timeout(unsigned long data) { @@ -838,7 +701,7 @@ static void fast_timer_test(void) int prev_num; int j; - struct timeval tv, tv0, tv1, tv2; + struct fasttime_t tv, tv0, tv1, tv2; printk("fast_timer_test() start\n"); do_gettimeofday_fast(&tv); @@ -851,21 +714,22 @@ static void fast_timer_test(void) { do_gettimeofday_fast(&tv_exp[j]); } - printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec); for (j = 0; j < 1000; j++) { - printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + printk(KERN_DEBUG "%i %i %i %i %i\n", + j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); j += 4; } for (j = 0; j < 100; j++) { - printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", - tv_exp[j].tv_sec,tv_exp[j].tv_usec, - tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, - tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, - tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, - tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, + tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec); j += 4; } do_gettimeofday_fast(&tv0); @@ -892,14 +756,15 @@ static void fast_timer_test(void) while (num_test_timeout < i) { if (num_test_timeout != prev_num) - { prev_num = num_test_timeout; - } } do_gettimeofday_fast(&tv2); - printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); - printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); - printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + printk(KERN_INFO "Timers started %is %06i\n", + tv0.tv_jiff, tv0.tv_usec); + printk(KERN_INFO "Timers started at %is %06i\n", + tv1.tv_jiff, tv1.tv_usec); + printk(KERN_INFO "Timers done %is %06i\n", + tv2.tv_jiff, tv2.tv_usec); DP(printk("buf0:\n"); printk(buf0); printk("buf1:\n"); @@ -921,9 +786,9 @@ static void fast_timer_test(void) printk("%-10s set: %6is %06ius exp: %6is %06ius " "data: 0x%08X func: 0x%08X\n", t->name, - t->tv_set.tv_sec, + t->tv_set.tv_jiff, t->tv_set.tv_usec, - t->tv_expires.tv_sec, + t->tv_expires.tv_jiff, t->tv_expires.tv_usec, t->data, t->function @@ -931,10 +796,12 @@ static void fast_timer_test(void) printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", t->delay_us, - tv_exp[j].tv_sec, + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, exp_num[j], - (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) * + 1000000 + tv_exp[j].tv_usec - + t->tv_expires.tv_usec); } proc_fasttimer_read(buf5, NULL, 0, 0, 0); printk("buf5 after all done:\n"); @@ -944,7 +811,7 @@ static void fast_timer_test(void) #endif -void fast_timer_init(void) +int fast_timer_init(void) { /* For some reason, request_irq() hangs when called froom time_init() */ if (!fast_timer_is_init) @@ -952,18 +819,20 @@ void fast_timer_init(void) printk("fast_timer_init()\n"); #ifdef CONFIG_PROC_FS - if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) - fasttimer_proc_entry->read_proc = proc_fasttimer_read; + fasttimer_proc_entry = create_proc_entry("fasttimer", 0, 0); + if (fasttimer_proc_entry) + fasttimer_proc_entry->read_proc = proc_fasttimer_read; #endif /* PROC_FS */ - if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, IRQF_DISABLED, - "fast timer int", NULL)) - { - printk("err: timer1 irq\n"); - } + if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "fast timer int", &fast_timer_list)) + printk(KERN_ERR "err: fasttimer irq\n"); fast_timer_is_init = 1; #ifdef FAST_TIMER_TEST printk("do test\n"); fast_timer_test(); #endif } + return 0; } +__initcall(fast_timer_init); diff --git a/arch/cris/arch-v32/kernel/head.S b/arch/cris/arch-v32/kernel/head.S index 20bd80a84e4..2d66a7c320e 100644 --- a/arch/cris/arch-v32/kernel/head.S +++ b/arch/cris/arch-v32/kernel/head.S @@ -4,22 +4,25 @@ * Copyright (C) 2003, Axis Communications AB */ - #define ASSEMBLER_MACROS_ONLY /* * The macros found in mmu_defs_asm.h uses the ## concatenation operator, so * -traditional must not be used when assembling this file. */ -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/asm/mmu_defs_asm.h> -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/config_defs_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/reg_rdwr.h> +#include <asm/arch/memmap.h> +#include <hwregs/intr_vect.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define CRAMFS_MAGIC 0x28cd3d45 +#define JHEAD_MAGIC 0x1FF528A6 +#define JHEAD_SIZE 8 #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 +#define NAND_BOOT_MAGIC 0x9a9db001 ;; NOTE: R8 and R9 carry information from the decompressor (if the ;; kernel was compressed). They must not be used in the code below @@ -30,12 +33,11 @@ .global romfs_start .global romfs_length .global romfs_in_flash + .global nand_boot .global swapper_pg_dir - .global crisv32_nand_boot - .global crisv32_nand_cramfs_offset ;; Dummy section to make it bootable with current VCS simulator -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM .section ".boot", "ax" ba tstart nop @@ -51,33 +53,15 @@ tstart: ;; 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] - - ;; Set up waitstates etc - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP1_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP2_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1 - move.d $r1, [$r0] - -#ifdef CONFIG_ETRAXFS_SIM - ;; Set up minimal flash waitstates - move.d 0, $r10 - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11 - move.d $r10, [$r11] + START_CLOCKS + + SETUP_WAIT_STATES + + GIO_INIT + +#ifdef CONFIG_SMP +secondary_cpu_entry: /* Entry point for secondary CPUs */ + di #endif ;; Setup and enable the MMU. Use same configuration for both the data @@ -85,7 +69,7 @@ tstart: ;; ;; Note; 3 cycles is needed for a bank-select to take effect. Further; ;; bank 1 is the instruction MMU, bank 2 is the data MMU. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0 @@ -93,7 +77,7 @@ tstart: ;; Map the virtual DRAM to the RW eprom area at address 0. ;; Also map 0xa for the hook calls, move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ - | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0 #endif @@ -104,7 +88,7 @@ tstart: ;; Enable certain page protections and setup linear mapping ;; for f,e,c,b,4,0. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_STATE(mmu, rw_mm_cfg, we, on) \ | REG_STATE(mmu, rw_mm_cfg, acc, on) \ | REG_STATE(mmu, rw_mm_cfg, ex, on) \ @@ -183,17 +167,11 @@ tstart: nop nop nop - move $s10, $r0 + move $s12, $r0 cmpq 0, $r0 beq master_cpu nop slave_cpu: - ; A slave waits for cpu_now_booting to be equal to CPU ID. - move.d cpu_now_booting, $r1 -slave_wait: - cmp.d [$r1], $r0 - bne slave_wait - nop ; Time to boot-up. Get stack location provided by master CPU. move.d smp_init_current_idle_thread, $r1 move.d [$r1], $sp @@ -203,9 +181,16 @@ slave_wait: jsr smp_callin nop master_cpu: + /* Set up entry point for secondary CPUs. The boot ROM has set up + * EBP at start of internal memory. The CPU will get there + * later when we issue an IPI to them... */ + move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0 + move.d secondary_cpu_entry, $r1 + move.d $r1, [$r0] #endif -#ifndef CONFIG_ETRAXFS_SIM - ;; Check if starting from DRAM or flash. +#ifndef CONFIG_ETRAX_VCS_SIM + ; Check if starting from DRAM (network->RAM boot or unpacked + ; compressed kernel), or directly from flash. lapcq ., $r0 and.d 0x7fffffff, $r0 ; Mask off the non-cache bit. cmp.d 0x10000, $r0 ; Arbitrary, something above this code. @@ -232,12 +217,13 @@ _inflash: beq _dram_initialized nop -#include "../lib/dram_init.S" +#include "../mach/dram_init.S" _dram_initialized: ;; Copy the text and data section to DRAM. This depends on that the ;; variables used below are correctly set up by the linker script. ;; The calculated value stored in R4 is used below. + ;; Leave the cramfs file system (piggybacked after the kernel) in flash. moveq 0, $r0 ; Source. move.d text_start, $r1 ; Destination. move.d __vmlinux_end, $r2 @@ -249,7 +235,7 @@ _dram_initialized: blo 1b nop - ;; Keep CRAMFS in flash. + ;; Check for cramfs. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] @@ -258,6 +244,7 @@ _dram_initialized: bne 1f nop + ;; Set length and start of cramfs, set romfs_in_flash flag addoq +4, $r4, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -273,35 +260,32 @@ _dram_initialized: nop _inram: - ;; Check if booting from NAND flash (in that case we just remember the offset - ;; into the flash where cramfs should be). - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne move_cramfs - moveq 1,$r0 - move.d crisv32_nand_boot, $r1 - move.d $r0, [$r1] - move.d crisv32_nand_cramfs_offset, $r1 - move.d $r9, [$r1] + ;; Check if booting from NAND flash; if so, set appropriate flags + ;; and move on. + cmp.d NAND_BOOT_MAGIC, $r12 + bne move_cramfs ; not nand, jump moveq 1, $r0 - move.d romfs_in_flash, $r1 + move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND + move.d $r0, [$r1] + moveq 0, $r0 ; tell axisflashmap romfs is not in + move.d romfs_in_flash, $r1 ; (directly accessed) flash move.d $r0, [$r1] - jump _start_it + jump _start_it ; continue with boot nop move_cramfs: - ;; Move the cramfs after BSS. + ;; kernel is in DRAM. + ;; Must figure out if there is a piggybacked rootfs image or not. + ;; Set romfs_length to 0 => no rootfs image available by default. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; The kernel could have been unpacked to DRAM by the loader, but - ;; the cramfs image could still be inte the flash immediately - ;; following the compressed kernel image. The loaded passes the address - ;; of the bute succeeding the last compressed byte in the flash in + ;; the cramfs image could still be in the flash immediately + ;; following the compressed kernel image. The loader passes the address + ;; of the byte succeeding the last compressed byte in the flash in ;; register R9 when starting the kernel. cmp.d 0x0ffffff8, $r9 bhs _no_romfs_in_flash ; R9 points outside the flash area. @@ -310,11 +294,13 @@ move_cramfs: ba _no_romfs_in_flash nop #endif + ;; cramfs rootfs might to be in flash. Check for it. move.d [$r9], $r0 ; cramfs_super.magic cmp.d CRAMFS_MAGIC, $r0 bne _no_romfs_in_flash nop + ;; found cramfs in flash. set address and size, and romfs_in_flash flag. addoq +4, $r9, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -330,27 +316,43 @@ move_cramfs: nop _no_romfs_in_flash: - ;; Look for cramfs. + ;; No romfs in flash, so look for cramfs, or jffs2 with jhead, + ;; after kernel in RAM, as is the case with network->RAM boot. + ;; For cramfs, partition starts with magic and length. + ;; For jffs2, a jhead is prepended which contains with magic and length. + ;; The jhead is not part of the jffs2 partition however. #ifndef CONFIG_ETRAXFS_SIM move.d __vmlinux_end, $r0 #else move.d __end, $r0 #endif move.d [$r0], $r1 - cmp.d CRAMFS_MAGIC, $r1 - bne 2f + cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic? + beq 2f ; yes, jump + nop + cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic? + bne 4f ; no, skip copy nop + addq 4, $r0 ; location of jffs2 size + move.d [$r0+], $r2 ; fetch jffs2 size -> r2 + ; r0 now points to start of jffs2 + ba 3f + nop +2: + addoq +4, $r0, $acr ; location of cramfs size + move.d [$acr], $r2 ; fetch cramfs size -> r2 + ; r0 still points to start of cramfs +3: + ;; Now, move the root fs to after kernel's BSS - addoq +4, $r0, $acr - move.d [$acr], $r2 - move.d _end, $r1 + move.d _end, $r1 ; start of cramfs -> r1 move.d romfs_start, $r3 - move.d $r1, [$r3] + move.d $r1, [$r3] ; store at romfs_start (for axisflashmap) move.d romfs_length, $r3 - move.d $r2, [$r3] + move.d $r2, [$r3] ; store size at romfs_length -#ifndef CONFIG_ETRAXFS_SIM - add.d $r2, $r0 +#ifndef CONFIG_ETRAX_VCS_SIM + add.d $r2, $r0 ; copy from end and downwards add.d $r2, $r1 lsrq 1, $r2 ; Size is in bytes, we copy words. @@ -365,10 +367,17 @@ _no_romfs_in_flash: nop #endif -2: +4: + ;; BSS move done. + ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM + ;; Also clear nand_boot flag; if we got here, we know we've not + ;; booted from NAND flash. moveq 0, $r0 move.d romfs_in_flash, $r1 move.d $r0, [$r1] + moveq 0, $r0 + move.d nand_boot, $r1 + move.d $r0, [$r1] jump _start_it ; Jump to cached code. nop @@ -384,8 +393,8 @@ _start_it: move.d cris_command_line, $r10 or.d 0x80000000, $r11 ; Make it virtual 1: - move.b [$r11+], $r12 - move.b $r12, [$r10+] + move.b [$r11+], $r1 + move.b $r1, [$r10+] subq 1, $r13 bne 1b nop @@ -401,7 +410,7 @@ no_command_line: move.d etrax_irv, $r1 ; Set the exception base register and pointer. move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; Clear the BSS region from _bss_start to _end. move.d __bss_start, $r0 move.d _end, $r1 @@ -411,7 +420,7 @@ no_command_line: nop #endif -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM /* Set the watchdog timeout to something big. Will be removed when */ /* watchdog can be disabled with command line option */ move.d 0x7fffffff, $r10 @@ -423,25 +432,44 @@ no_command_line: move.d __bss_start, $r0 movem [$r0], $r13 +#ifdef CONFIG_ETRAX_L2CACHE + jsr l2cache_init + nop +#endif + jump start_kernel ; Jump to start_kernel() in init/main.c. nop .data etrax_irv: .dword 0 + +; Variables for communication with the Axis flash map driver (axisflashmap), +; and for setting up memory in arch/cris/kernel/setup.c . + +; romfs_start is set to the start of the root file system, if it exists +; in directly accessible memory (i.e. NOR Flash when booting from Flash, +; or RAM when booting directly from a network-downloaded RAM image) romfs_start: .dword 0 + +; romfs_length is set to the size of the root file system image, if it exists +; in directly accessible memory (see romfs_start). Otherwise it is set to 0. romfs_length: .dword 0 + +; romfs_in_flash is set to 1 if the root file system resides in directly +; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot +; or NAND flash boot. romfs_in_flash: .dword 0 -crisv32_nand_boot: - .dword 0 -crisv32_nand_cramfs_offset: + +; nand_boot is set to 1 when the kernel has been booted from NAND flash +nand_boot: .dword 0 swapper_pg_dir = 0xc0002000 .section ".init.data", "aw" -#include "../lib/hw_settings.S" +#include "../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/kernel/io.c b/arch/cris/arch-v32/kernel/io.c deleted file mode 100644 index a22a9e02e09..00000000000 --- a/arch/cris/arch-v32/kernel/io.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Helper functions for I/O pins. - * - * Copyright (c) 2004 Axis Communications AB. - */ - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/string.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/io.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/gio_defs.h> - -struct crisv32_ioport crisv32_ioports[] = -{ - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pa_din), - 8 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pb_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pc_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pd_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din), - 18 - } -}; - -#define NBR_OF_PORTS ARRAY_SIZE(crisv32_ioports) - -struct crisv32_iopin crisv32_led1_green; -struct crisv32_iopin crisv32_led1_red; -struct crisv32_iopin crisv32_led2_green; -struct crisv32_iopin crisv32_led2_red; -struct crisv32_iopin crisv32_led3_green; -struct crisv32_iopin crisv32_led3_red; - -/* Dummy port used when green LED and red LED is on the same bit */ -static unsigned long io_dummy; -static struct crisv32_ioport dummy_port = -{ - &io_dummy, - &io_dummy, - &io_dummy, - 18 -}; -static struct crisv32_iopin dummy_led = -{ - &dummy_port, - 0 -}; - -static int __init crisv32_io_init(void) -{ - int ret = 0; - /* Initialize LEDs */ - ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G); - ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R); - ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G); - ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R); - ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G); - ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R); - crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); - - if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R)) - crisv32_led1_red = dummy_led; - if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R)) - crisv32_led2_red = dummy_led; - - return ret; -} - -__initcall(crisv32_io_init); - -int crisv32_io_get(struct crisv32_iopin* iopin, - unsigned int port, unsigned int pin) -{ - if (port > NBR_OF_PORTS) - return -EINVAL; - if (port > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -int crisv32_io_get_name(struct crisv32_iopin* iopin, - char* name) -{ - int port; - int pin; - - if (toupper(*name) == 'P') - name++; - - if (toupper(*name) < 'A' || toupper(*name) > 'E') - return -EINVAL; - - port = toupper(*name) - 'A'; - name++; - pin = simple_strtoul(name, NULL, 10); - - if (pin < 0 || pin > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -#ifdef CONFIG_PCI -/* PCI I/O access stuff */ -struct cris_io_operations* cris_iops = NULL; -EXPORT_SYMBOL(cris_iops); -#endif - diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c index a9acaa27024..173c141ac9b 100644 --- a/arch/cris/arch-v32/kernel/irq.c +++ b/arch/cris/arch-v32/kernel/irq.c @@ -15,15 +15,21 @@ #include <linux/threads.h> #include <linux/spinlock.h> #include <linux/kernel_stat.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #define CPU_FIXED -1 /* IRQ masks (refer to comment for crisv32_do_multiple) */ -#define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ)) +#if TIMER0_INTR_VECT - FIRST_IRQ < 32 +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ)) +#undef TIMER_VECT1 +#else +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32)) +#define TIMER_VECT1 +#endif #ifdef CONFIG_ETRAX_KGDB #if defined(CONFIG_ETRAX_KGDB_PORT0) #define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ)) @@ -44,8 +50,8 @@ struct cris_irq_allocation cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ }; -struct cris_irq_allocation irq_allocations[NR_IRQS] = - {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; +struct cris_irq_allocation irq_allocations[NR_REAL_IRQS] = + { [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} }; static unsigned long irq_regs[NR_CPUS] = { @@ -55,6 +61,12 @@ static unsigned long irq_regs[NR_CPUS] = #endif }; +#if NR_REAL_IRQS > 32 +#define NBR_REGS 2 +#else +#define NBR_REGS 1 +#endif + unsigned long cpu_irq_counters[NR_CPUS]; unsigned long irq_counters[NR_REAL_IRQS]; @@ -79,45 +91,81 @@ extern void d_mmu_write(void); extern void kgdb_init(void); extern void breakpoint(void); +/* From traps.c. */ +extern void breakh_BUG(void); + /* - * Build the IRQ handler stubs using macros from irq.h. First argument is the - * IRQ number, the second argument is the corresponding bit in - * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. + * Build the IRQ handler stubs using macros from irq.h. */ -BUILD_IRQ(0x31, (1 << 0)) /* memarb */ -BUILD_IRQ(0x32, (1 << 1)) /* gen_io */ -BUILD_IRQ(0x33, (1 << 2)) /* iop0 */ -BUILD_IRQ(0x34, (1 << 3)) /* iop1 */ -BUILD_IRQ(0x35, (1 << 4)) /* iop2 */ -BUILD_IRQ(0x36, (1 << 5)) /* iop3 */ -BUILD_IRQ(0x37, (1 << 6)) /* dma0 */ -BUILD_IRQ(0x38, (1 << 7)) /* dma1 */ -BUILD_IRQ(0x39, (1 << 8)) /* dma2 */ -BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */ -BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */ -BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */ -BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */ -BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */ -BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */ -BUILD_IRQ(0x40, (1 << 15)) /* dma9 */ -BUILD_IRQ(0x41, (1 << 16)) /* ata */ -BUILD_IRQ(0x42, (1 << 17)) /* sser0 */ -BUILD_IRQ(0x43, (1 << 18)) /* sser1 */ -BUILD_IRQ(0x44, (1 << 19)) /* ser0 */ -BUILD_IRQ(0x45, (1 << 20)) /* ser1 */ -BUILD_IRQ(0x46, (1 << 21)) /* ser2 */ -BUILD_IRQ(0x47, (1 << 22)) /* ser3 */ -BUILD_IRQ(0x48, (1 << 23)) -BUILD_IRQ(0x49, (1 << 24)) /* eth0 */ -BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */ -BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */ -BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */ -BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */ -BUILD_IRQ(0x4e, (1 << 29)) /* ext */ -BUILD_IRQ(0x4f, (1 << 29)) /* ipi */ +BUILD_IRQ(0x31) +BUILD_IRQ(0x32) +BUILD_IRQ(0x33) +BUILD_IRQ(0x34) +BUILD_IRQ(0x35) +BUILD_IRQ(0x36) +BUILD_IRQ(0x37) +BUILD_IRQ(0x38) +BUILD_IRQ(0x39) +BUILD_IRQ(0x3a) +BUILD_IRQ(0x3b) +BUILD_IRQ(0x3c) +BUILD_IRQ(0x3d) +BUILD_IRQ(0x3e) +BUILD_IRQ(0x3f) +BUILD_IRQ(0x40) +BUILD_IRQ(0x41) +BUILD_IRQ(0x42) +BUILD_IRQ(0x43) +BUILD_IRQ(0x44) +BUILD_IRQ(0x45) +BUILD_IRQ(0x46) +BUILD_IRQ(0x47) +BUILD_IRQ(0x48) +BUILD_IRQ(0x49) +BUILD_IRQ(0x4a) +BUILD_IRQ(0x4b) +BUILD_IRQ(0x4c) +BUILD_IRQ(0x4d) +BUILD_IRQ(0x4e) +BUILD_IRQ(0x4f) +BUILD_IRQ(0x50) +#if MACH_IRQS > 32 +BUILD_IRQ(0x51) +BUILD_IRQ(0x52) +BUILD_IRQ(0x53) +BUILD_IRQ(0x54) +BUILD_IRQ(0x55) +BUILD_IRQ(0x56) +BUILD_IRQ(0x57) +BUILD_IRQ(0x58) +BUILD_IRQ(0x59) +BUILD_IRQ(0x5a) +BUILD_IRQ(0x5b) +BUILD_IRQ(0x5c) +BUILD_IRQ(0x5d) +BUILD_IRQ(0x5e) +BUILD_IRQ(0x5f) +BUILD_IRQ(0x60) +BUILD_IRQ(0x61) +BUILD_IRQ(0x62) +BUILD_IRQ(0x63) +BUILD_IRQ(0x64) +BUILD_IRQ(0x65) +BUILD_IRQ(0x66) +BUILD_IRQ(0x67) +BUILD_IRQ(0x68) +BUILD_IRQ(0x69) +BUILD_IRQ(0x6a) +BUILD_IRQ(0x6b) +BUILD_IRQ(0x6c) +BUILD_IRQ(0x6d) +BUILD_IRQ(0x6e) +BUILD_IRQ(0x6f) +BUILD_IRQ(0x70) +#endif /* Pointers to the low-level handlers. */ -static void (*interrupt[NR_IRQS])(void) = { +static void (*interrupt[MACH_IRQS])(void) = { IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt, IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt, IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt, @@ -128,7 +176,20 @@ static void (*interrupt[NR_IRQS])(void) = { IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt, IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt, IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt, - IRQ0x4f_interrupt + IRQ0x4f_interrupt, IRQ0x50_interrupt, +#if MACH_IRQS > 32 + IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt, + IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt, + IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt, + IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt, + IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt, + IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt, + IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt, + IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt, + IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt, + IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt, + IRQ0x6f_interrupt, IRQ0x70_interrupt, +#endif }; void @@ -137,13 +198,26 @@ block_irq(int irq, int cpu) int intr_mask; unsigned long flags; - spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask &= ~(1 << (irq - FIRST_IRQ)); - - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); + spin_lock_irqsave(&irq_lock, flags); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask &= ~(1 << (irq - FIRST_IRQ)); + else + intr_mask &= ~(1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -154,12 +228,26 @@ unblock_irq(int irq, int cpu) unsigned long flags; spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask |= (1 << (irq - FIRST_IRQ)); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask |= (1 << (irq - FIRST_IRQ)); + else + intr_mask |= (1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -298,8 +386,9 @@ crisv32_do_multiple(struct pt_regs* regs) { int cpu; int mask; - int masked; + int masked[NBR_REGS]; int bit; + int i; cpu = smp_processor_id(); @@ -308,42 +397,59 @@ crisv32_do_multiple(struct pt_regs* regs) */ irq_enter(); - /* Get which IRQs that happened. */ - masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); + for (i = 0; i < NBR_REGS; i++) { + /* Get which IRQs that happend. */ + masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + r_masked_vect, i); - /* Calculate new IRQ mask with these IRQs disabled. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask &= ~masked; + /* Calculate new IRQ mask with these IRQs disabled. */ + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask &= ~masked[i]; /* Timer IRQ is never masked */ - if (masked & TIMER_MASK) - mask |= TIMER_MASK; - - /* Block all the IRQs */ - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#else + if ((i == 0) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#endif + /* Block all the IRQs */ + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); /* Check for timer IRQ and handle it special. */ - if (masked & TIMER_MASK) { - masked &= ~TIMER_MASK; - do_IRQ(TIMER_INTR_VECT, regs); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } +#else + if ((i == 0) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } } +#endif #ifdef IGNORE_MASK /* Remove IRQs that can't be handled as multiple. */ - masked &= ~IGNORE_MASK; + masked[0] &= ~IGNORE_MASK; #endif /* Handle the rest of the IRQs. */ - for (bit = 0; bit < 32; bit++) - { - if (masked & (1 << bit)) - do_IRQ(bit + FIRST_IRQ, regs); + for (i = 0; i < NBR_REGS; i++) { + for (bit = 0; bit < 32; bit++) { + if (masked[i] & (1 << bit)) + do_IRQ(bit + FIRST_IRQ + i*32, regs); + } } /* Unblock all the IRQs. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask |= masked; - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); + for (i = 0; i < NBR_REGS; i++) { + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask |= masked[i]; + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); + } /* This irq_exit() will trigger the soft IRQs. */ irq_exit(); @@ -361,20 +467,21 @@ init_IRQ(void) reg_intr_vect_rw_mask vect_mask = {0}; /* Clear all interrupts masks. */ - REG_WR(intr_vect, regi_irq, rw_mask, vect_mask); + for (i = 0; i < NBR_REGS; i++) + REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask); for (i = 0; i < 256; i++) etrax_irv->v[i] = weird_irq; - /* Point all IRQs to bad handlers. */ + /* Point all IRQ's to bad handlers. */ for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { irq_desc[j].chip = &crisv32_irq_type; set_exception_vector(i, interrupt[j]); } /* Mark Timer and IPI IRQs as CPU local */ - irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; - irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; + irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; + irq_desc[TIMER0_INTR_VECT].status |= IRQ_PER_CPU; irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; @@ -391,6 +498,11 @@ init_IRQ(void) set_exception_vector(0x0a, d_mmu_access); set_exception_vector(0x0b, d_mmu_write); +#ifdef CONFIG_BUG + /* Break 14 handler, used to implement cheap BUG(). */ + set_exception_vector(0x1e, breakh_BUG); +#endif + /* The system-call trap is reached by "break 13". */ set_exception_vector(0x1d, system_call); diff --git a/arch/cris/arch-v32/kernel/kgdb.c b/arch/cris/arch-v32/kernel/kgdb.c index 480e56348be..4e2e2e271ef 100644 --- a/arch/cris/arch-v32/kernel/kgdb.c +++ b/arch/cris/arch-v32/kernel/kgdb.c @@ -381,7 +381,7 @@ static int read_register(char regno, unsigned int *valptr); /* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ int getDebugChar(void); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM int getDebugChar(void) { return socketread(); @@ -391,7 +391,7 @@ int getDebugChar(void) /* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ void putDebugChar(int val); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM void putDebugChar(int val) { socketwrite((char *)&val, 1); @@ -1599,7 +1599,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT1) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1611,7 +1611,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT2) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1623,7 +1623,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT3) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1635,7 +1635,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask); #endif diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c index b72a15580dc..ced5b725d9b 100644 --- a/arch/cris/arch-v32/kernel/process.c +++ b/arch/cris/arch-v32/kernel/process.c @@ -12,17 +12,13 @@ #include <linux/err.h> #include <linux/fs.h> #include <linux/slab.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> extern void stop_watchdog(void); -#ifdef CONFIG_ETRAX_GPIO -extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */ -#endif - extern int cris_hlt_counter; /* We use this if we don't have any better idle routine. */ @@ -82,7 +78,7 @@ hard_reset_now(void) wd_ctrl.cmd = regk_timer_start; arch_enable_nmi(); - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index 2df60529a8a..e27f4670e88 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2003, Axis Communications AB. + * Copyright (C) 2000-2007, Axis Communications AB. */ #include <linux/kernel.h> @@ -149,7 +149,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = generic_ptrace_pokedata(child, addr, data); break; - /* Write the word at location address in the USER area. */ + /* Write the word at location address in the USER area. */ case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) @@ -201,7 +201,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; - /* Make the child exit by sending it a sigkill. */ + /* Make the child exit by sending it a sigkill. */ case PTRACE_KILL: ret = 0; @@ -245,9 +245,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } + /* Get all GP registers from the child. */ case PTRACE_GETREGS: { - int i; + int i; unsigned long tmp; for (i = 0; i <= PT_MAX; i++) { @@ -294,6 +295,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } +out_tsk: return ret; } diff --git a/arch/cris/arch-v32/kernel/signal.c b/arch/cris/arch-v32/kernel/signal.c index 024cc690197..58c1866804e 100644 --- a/arch/cris/arch-v32/kernel/signal.c +++ b/arch/cris/arch-v32/kernel/signal.c @@ -50,7 +50,7 @@ struct rt_signal_frame { unsigned char retcode[8]; /* Trampoline code. */ }; -int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs); +void do_signal(int restart, struct pt_regs *regs); void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs); /* @@ -61,74 +61,16 @@ int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - sigset_t saveset; - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); - - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* - * This point is reached twice: once to call - * the signal handler, then again to return - * from the sigsuspend system call. When - * calling the signal handler, R10 hold the - * signal number as set by do_signal(). The - * sigsuspend call will always return with - * the restored value above; -EINTR. - */ - return regs->r10; - } - } -} - -/* Define some dummy arguments to be able to reach the regs argument. */ -int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, - long mof, long srp, struct pt_regs *regs) -{ - sigset_t saveset; - sigset_t newset; - - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* See comment in function above. */ - return regs->r10; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } int @@ -290,7 +232,7 @@ sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, goto badframe; if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) - goto badframe; + goto badframe; keep_debug_flags(oldccs, oldspc, regs); @@ -347,11 +289,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) /* Grab and setup a signal frame. * * Basically a lot of state-info is stacked, and arranged for the - * user-mode program to return to the kernel using either a trampoline + * user-mode program to return to the kernel using either a trampiline * which performs the syscall sigreturn(), or a provided user-mode * trampoline. */ -static void +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { @@ -417,16 +359,17 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Actually move the USP to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } -static void +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { @@ -503,21 +446,24 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Actually move the usp to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } /* Invoke a singal handler to, well, handle the signal. */ -static inline void +static inline int handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Check if this got called from a system call. */ if (canrestart) { /* If so, check system call restarting. */ @@ -561,19 +507,24 @@ handle_signal(int canrestart, unsigned long sig, /* Set up the stack frame. */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -587,12 +538,13 @@ handle_signal(int canrestart, unsigned long sig, * we can use user_mode(regs) to see if we came directly from kernel or user * mode below. */ -int -do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +void +do_signal(int canrestart, struct pt_regs *regs) { int signr; siginfo_t info; struct k_sigaction ka; + sigset_t *oldset; /* * The common case should go fast, which is why this point is @@ -600,17 +552,28 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) * without doing anything. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Deliver the signal. */ - handle_signal(canrestart, signr, &info, &ka, oldset, regs); - return 1; + /* Whee! Actually deliver the signal. */ + if (handle_signal(canrestart, signr, &info, &ka, + oldset, regs)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; } /* Got here from a system call? */ @@ -628,7 +591,12 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) } } - return 0; + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } asmlinkage void @@ -641,7 +609,7 @@ ugdb_trap_user(struct thread_info *ti, int sig) user_regs(ti)->spc = 0; } /* FIXME: Filter out false h/w breakpoint hits (i.e. EDA - not within any configured h/w breakpoint range). Synchronize with + not withing any configured h/w breakpoint range). Synchronize with what already exists for kernel debugging. */ if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) { /* Break 8: subtract 2 from ERP unless in a delay slot. */ diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index 171c96e0a5d..a9c3334e46c 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -1,11 +1,12 @@ +#include <linux/types.h> #include <asm/delay.h> -#include <asm/arch/irq.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <irq.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #include <asm/tlbflush.h> #include <asm/mmu_context.h> -#include <asm/arch/hwregs/mmu_defs_asm.h> -#include <asm/arch/hwregs/supp_reg.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/supp_reg.h> #include <asm/atomic.h> #include <linux/err.h> @@ -20,6 +21,7 @@ #define IPI_SCHEDULE 1 #define IPI_CALL 2 #define IPI_FLUSH_TLB 4 +#define IPI_BOOT 8 #define FLUSH_ALL (void*)0xffffffff @@ -30,6 +32,8 @@ spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED}; cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); cpumask_t phys_cpu_present_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map; +EXPORT_SYMBOL(cpu_possible_map); EXPORT_SYMBOL(phys_cpu_present_map); /* Variables used during SMP boot */ @@ -55,13 +59,12 @@ static unsigned long flush_addr; extern int setup_irq(int, struct irqaction *); /* Mode registers */ -static unsigned long irq_regs[NR_CPUS] = -{ +static unsigned long irq_regs[NR_CPUS] = { regi_irq, regi_irq2 }; -static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id); static int send_ipi(int vector, int wait, cpumask_t cpu_mask); static struct irqaction irq_ipi = { .handler = crisv32_ipi_interrupt, @@ -101,6 +104,7 @@ void __devinit smp_prepare_boot_cpu(void) cpu_set(0, cpu_online_map); cpu_set(0, phys_cpu_present_map); + cpu_set(0, cpu_possible_map); } void __init smp_cpus_done(unsigned int max_cpus) @@ -113,6 +117,7 @@ smp_boot_one_cpu(int cpuid) { unsigned timeout; struct task_struct *idle; + cpumask_t cpu_mask = CPU_MASK_NONE; idle = fork_idle(cpuid); if (IS_ERR(idle)) @@ -124,6 +129,12 @@ smp_boot_one_cpu(int cpuid) smp_init_current_idle_thread = task_thread_info(idle); cpu_now_booting = cpuid; + /* Kick it */ + cpu_set(cpuid, cpu_online_map); + cpu_set(cpuid, cpu_mask); + send_ipi(IPI_BOOT, 0, cpu_mask); + cpu_clear(cpuid, cpu_online_map); + /* Wait for CPU to come online */ for (timeout = 0; timeout < 10000; timeout++) { if(cpu_online(cpuid)) { @@ -165,7 +176,7 @@ void __init smp_callin(void) /* Enable IRQ and idle */ REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask); unmask_irq(IPI_INTR_VECT); - unmask_irq(TIMER_INTR_VECT); + unmask_irq(TIMER0_INTR_VECT); preempt_disable(); local_irq_enable(); @@ -328,7 +339,7 @@ int smp_call_function(void (*func)(void *info), void *info, return ret; } -irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id) { void (*func) (void *info) = call_data->func; void *info = call_data->info; diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 2f7e8e200f2..3a13dd6e0a9 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c @@ -1,8 +1,7 @@ -/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $ - * +/* * linux/arch/cris/arch-v32/kernel/time.c * - * Copyright (C) 2003 Axis Communications AB + * Copyright (C) 2003-2007 Axis Communications AB * */ @@ -14,28 +13,34 @@ #include <linux/sched.h> #include <linux/init.h> #include <linux/threads.h> +#include <linux/cpufreq.h> #include <asm/types.h> #include <asm/signal.h> #include <asm/io.h> #include <asm/delay.h> #include <asm/rtc.h> #include <asm/irq.h> - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <asm/irq_regs.h> + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* Watchdog defines */ -#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ -#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ -#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ +#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ +/* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) unsigned long timer_regs[NR_CPUS] = { - regi_timer, + regi_timer0, #ifdef CONFIG_SMP - regi_timer2 + regi_timer2 #endif }; @@ -44,12 +49,22 @@ extern int set_rtc_mmss(unsigned long nowtime); extern int setup_irq(int, struct irqaction *); extern int have_rtc; +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_time_freq_notifier_block = { + .notifier_call = cris_time_freq_notifier, +}; +#endif + unsigned long get_ns_in_jiffie(void) { reg_timer_r_tmr0_data data; unsigned long ns; - data = REG_RD(timer, regi_timer, r_tmr0_data); + data = REG_RD(timer, regi_timer0, r_tmr0_data); ns = (TIMER0_DIV - data) * 10; return ns; } @@ -59,31 +74,27 @@ unsigned long do_slow_gettimeoffset(void) unsigned long count; unsigned long usec_count = 0; - static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + /* For the first call after boot */ + static unsigned long count_p = TIMER0_DIV; static unsigned long jiffies_p = 0; - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ + /* Cache volatile jiffies temporarily; we have IRQs turned off. */ unsigned long jiffies_t; /* The timer interrupt comes from Etrax timer 0. In order to get * better precision, we check the current value. It might have - * underflowed already though. - */ + * underflowed already though. */ + count = REG_RD(timer, regi_timer0, r_tmr0_data); + jiffies_t = jiffies; - count = REG_RD(timer, regi_timer, r_tmr0_data); - jiffies_t = jiffies; - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are one problem that must be avoided here: - * 1. the timer counter underflows + /* Avoiding timer inconsistencies (they are rare, but they happen) + * There is one problem that must be avoided here: + * 1. the timer counter underflows */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { - /* Timer wrapped, use new count and prescale - * increase the time corresponding to one jiffie + /* Timer wrapped, use new count and prescale. + * Increase the time corresponding to one jiffy. */ usec_count = 1000000/HZ; } @@ -106,17 +117,15 @@ unsigned long do_slow_gettimeoffset(void) */ /* This gives us 1.3 ms to do something useful when the NMI comes */ -/* right now, starting the watchdog is the same as resetting it */ +/* Right now, starting the watchdog is the same as resetting it */ #define start_watchdog reset_watchdog #if defined(CONFIG_ETRAX_WATCHDOG) static short int watchdog_key = 42; /* arbitrary 7 bit number */ #endif -/* number of pages to consider "out of memory". it is normal that the memory - * is used though, so put this really low. - */ - +/* Number of pages to consider "out of memory". It is normal that the memory + * is used though, so set this really low. */ #define WATCHDOG_MIN_FREE_PAGES 8 void @@ -125,14 +134,15 @@ reset_watchdog(void) #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; - /* only keep watchdog happy as long as we have memory left! */ + /* Only keep watchdog happy as long as we have memory left! */ if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { - /* reset the watchdog with the inverse of the old key */ - watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */ + /* Reset the watchdog with the inverse of the old key */ + /* Invert key, which is 7 bits */ + watchdog_key ^= ETRAX_WD_KEY_MASK; wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_start; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif } @@ -148,7 +158,7 @@ stop_watchdog(void) wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_stop; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); #endif } @@ -160,17 +170,28 @@ handle_watchdog_bite(struct pt_regs* regs) #if defined(CONFIG_ETRAX_WATCHDOG) extern int cause_of_death; - raw_printk("Watchdog bite\n"); + oops_in_progress = 1; + printk(KERN_WARNING "Watchdog bite\n"); /* Check if forced restart or unexpected watchdog */ if (cause_of_death == 0xbedead) { +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + /* There is a bug in Artpec-3 (voodoo TR 78) that requires + * us to go to lower frequency for the reset to be reliable + */ + reg_clkgen_rw_clk_ctrl ctrl = + REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl); +#endif while(1); } - /* Unexpected watchdog, stop the watchdog and dump registers*/ + /* Unexpected watchdog, stop the watchdog and dump registers. */ stop_watchdog(); - raw_printk("Oops: bitten by watchdog\n"); - show_registers(regs); + printk(KERN_WARNING "Oops: bitten by watchdog\n"); + show_registers(regs); + oops_in_progress = 0; #ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY reset_watchdog(); #endif @@ -178,21 +199,19 @@ handle_watchdog_bite(struct pt_regs* regs) #endif } -/* last time the cmos clock got updated */ +/* Last time the cmos clock got updated. */ static long last_rtc_update = 0; /* * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick + * as well as call the "do_timer()" routine every clocktick. */ - -//static unsigned short myjiff; /* used by our debug routine print_timestamp */ - extern void cris_do_profile(struct pt_regs *regs); static inline irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_interrupt(int irq, void *dev_id) { + struct pt_regs *regs = get_irq_regs(); int cpu = smp_processor_id(); reg_timer_r_masked_intr masked_intr; reg_timer_rw_ack_intr ack_intr = { 0 }; @@ -202,11 +221,11 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (!masked_intr.tmr0) return IRQ_NONE; - /* acknowledge the timer irq */ + /* Acknowledge the timer irq. */ ack_intr.tmr0 = 1; REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr); - /* reset watchdog otherwise it resets us! */ + /* Reset watchdog otherwise it resets us! */ reset_watchdog(); /* Update statistics. */ @@ -218,7 +237,7 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (cpu != 0) return IRQ_HANDLED; - /* call the real timer interrupt handler */ + /* Call the real timer interrupt handler */ do_timer(1); /* @@ -236,17 +255,17 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + /* Do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; } return IRQ_HANDLED; } -/* timer is IRQF_SHARED so drivers can add stuff to the timer irq chain - * it needs to be IRQF_DISABLED to make the jiffies update work properly +/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. + * It needs to be IRQF_DISABLED to make the jiffies update work properly. */ - -static struct irqaction irq_timer = { - .mask = timer_interrupt, +static struct irqaction irq_timer = { + .handler = timer_interrupt, .flags = IRQF_SHARED | IRQF_DISABLED, .mask = CPU_MASK_NONE, .name = "timer" @@ -256,27 +275,27 @@ void __init cris_timer_init(void) { int cpu = smp_processor_id(); - reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; - reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; + reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; + reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; reg_timer_rw_intr_mask timer_intr_mask; - /* Setup the etrax timers + /* Setup the etrax timers. * Base frequency is 100MHz, divider 1000000 -> 100 HZ * We use timer0, so timer1 is free. * The trig timer is used by the fasttimer API if enabled. */ - tmr0_ctrl.op = regk_timer_ld; + tmr0_ctrl.op = regk_timer_ld; tmr0_ctrl.freq = regk_timer_f100; - REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ - tmr0_ctrl.op = regk_timer_run; - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ - - /* enable the timer irq */ - timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); - timer_intr_mask.tmr0 = 1; - REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); + REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ + tmr0_ctrl.op = regk_timer_run; + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ + + /* Enable the timer irq. */ + timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); + timer_intr_mask.tmr0 = 1; + REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); } void __init @@ -284,7 +303,7 @@ time_init(void) { reg_intr_vect_rw_mask intr_mask; - /* probe for the RTC and read it if it exists + /* Probe for the RTC and read it if it exists. * Before the RTC can be probed the loops_per_usec variable needs * to be initialized to make usleep work. A better value for * loops_per_usec is calculated by the kernel later once the @@ -293,52 +312,74 @@ time_init(void) loops_per_usec = 50; if(RTC_INIT() < 0) { - /* no RTC, start at 1980 */ + /* No RTC, start at 1980 */ xtime.tv_sec = 0; xtime.tv_nsec = 0; have_rtc = 0; } else { - /* get the current time */ + /* Get the current time */ have_rtc = 1; update_xtime_from_cmos(); } /* - * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the - * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC). + * Initialize wall_to_monotonic such that adding it to + * xtime will yield zero, the tv_nsec field must be normalized + * (i.e., 0 <= nsec < NSEC_PER_SEC). */ set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - /* Start CPU local timer */ + /* Start CPU local timer. */ cris_timer_init(); - /* enable the timer irq in global config */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.timer = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - - /* now actually register the timer irq handler that calls timer_interrupt() */ + /* Enable the timer irq in global config. */ + intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1); + intr_mask.timer0 = 1; + REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask); - setup_irq(TIMER_INTR_VECT, &irq_timer); + /* Now actually register the timer irq handler that calls + * timer_interrupt(). */ + setup_irq(TIMER0_INTR_VECT, &irq_timer); - /* enable watchdog if we should use one */ + /* Enable watchdog if we should use one. */ #if defined(CONFIG_ETRAX_WATCHDOG) - printk("Enabling watchdog...\n"); + printk(KERN_INFO "Enabling watchdog...\n"); start_watchdog(); /* If we use the hardware watchdog, we want to trap it as an NMI - and dump registers before it resets us. For this to happen, we - must set the "m" NMI enable flag (which once set, is unset only - when an NMI is taken). - - The same goes for the external NMI, but that doesn't have any - driver or infrastructure support yet. */ - { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); - } + * and dump registers before it resets us. For this to happen, we + * must set the "m" NMI enable flag (which once set, is unset only + * when an NMI is taken). */ + { + unsigned long flags; + local_save_flags(flags); + flags |= (1<<30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); + } +#endif + +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&cris_time_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); #endif } + +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_POSTCHANGE) { + reg_timer_r_tmr0_data data; + reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ; + do { + data = REG_RD(timer, timer_regs[freqs->cpu], + r_tmr0_data); + } while (data > 20); + REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div); + } + return 0; +} +#endif diff --git a/arch/cris/arch-v32/kernel/traps.c b/arch/cris/arch-v32/kernel/traps.c index 17fd3dbd1c8..9003e382cad 100644 --- a/arch/cris/arch-v32/kernel/traps.c +++ b/arch/cris/arch-v32/kernel/traps.c @@ -1,50 +1,45 @@ /* - * Copyright (C) 2003, Axis Communications AB. + * Copyright (C) 2003-2006, Axis Communications AB. */ #include <linux/ptrace.h> +#include <linux/module.h> #include <asm/uaccess.h> - -#include <asm/arch/hwregs/supp_reg.h> - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - -extern int raw_printk(const char *fmt, ...); +#include <hwregs/supp_reg.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/irq.h> void show_registers(struct pt_regs *regs) { /* * It's possible to use either the USP register or current->thread.usp. - * USP might not correspond to the current proccess for all cases this + * USP might not correspond to the current process for all cases this * function is called, and current->thread.usp isn't up to date for the - * current proccess. Experience shows that using USP is the way to go. + * current process. Experience shows that using USP is the way to go. */ - unsigned long usp; + unsigned long usp = rdusp(); unsigned long d_mmu_cause; unsigned long i_mmu_cause; - usp = rdusp(); + printk("CPU: %d\n", smp_processor_id()); - raw_printk("CPU: %d\n", smp_processor_id()); + printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); - raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", - regs->erp, regs->srp, regs->ccs, usp, regs->mof); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", - regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", - regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", - regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs->acr); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs->acr); - - raw_printk("sp: %08lx\n", regs); + printk(" sp: %08lx\n", (unsigned long)regs); SUPP_BANK_SEL(BANK_IM); SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause); @@ -52,18 +47,20 @@ show_registers(struct pt_regs *regs) SUPP_BANK_SEL(BANK_DM); SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause); - raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause); - raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); + printk(" Data MMU Cause: %08lx\n", d_mmu_cause); + printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); - raw_printk("Process %s (pid: %d, stackpage: %08lx)\n", - current->comm, current->pid, (unsigned long) current); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); - /* Show additional info if in kernel-mode. */ + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ if (!user_mode(regs)) { int i; - unsigned char c; - show_stack(NULL, (unsigned long *) usp); + show_stack(NULL, (unsigned long *)usp); /* * If the previous stack-dump wasn't a kernel one, dump the @@ -72,7 +69,7 @@ show_registers(struct pt_regs *regs) if (usp != 0) show_stack(NULL, NULL); - raw_printk("\nCode: "); + printk("\nCode: "); if (regs->erp < PAGE_OFFSET) goto bad_value; @@ -84,76 +81,115 @@ show_registers(struct pt_regs *regs) * instruction decoding should be in sync at the interesting * point, but small enough to fit on a row. The regs->erp * location is pointed out in a ksymoops-friendly way by - * wrapping the byte for that address in parenthesis. + * wrapping the byte for that address in parenthesises. */ for (i = -12; i < 12; i++) { - if (__get_user(c, &((unsigned char *) regs->erp)[i])) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->erp)[i])) { bad_value: - raw_printk(" Bad IP value."); + printk(" Bad IP value."); break; } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); + printk("%02x ", c); } - - raw_printk("\n"); + printk("\n"); } } -/* - * This gets called from entry.S when the watchdog has bitten. Show something - * similar to an Oops dump, and if the kernel is configured to be a nice doggy; - * halt instead of reboot. - */ void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); + unsigned long flags; - while (1) - ; /* Do nothing. */ -#else - show_registers(regs); -#endif + local_save_flags(flags); + flags |= (1 << 30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); } -/* This is normally the Oops function. */ -void -die_if_kernel(const char *str, struct pt_regs *regs, long err) +extern void (*nmi_handler)(struct pt_regs *); +void handle_nmi(struct pt_regs *regs) { - if (user_mode(regs)) - return; +#ifdef CONFIG_ETRAXFS + reg_intr_vect_r_nmi r; +#endif -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* - * This printout might take too long and could trigger - * the watchdog normally. If NICE_DOGGY is set, simply - * stop the watchdog during the printout. - */ - stop_watchdog(); + if (nmi_handler) + nmi_handler(regs); + +#ifdef CONFIG_ETRAXFS + /* Wait until nmi is no longer active. */ + do { + r = REG_RD(intr_vect, regi_irq, r_nmi); + } while (r.ext == regk_intr_vect_on); #endif +} - raw_printk("%s: %04lx\n", str, err & 0xffff); - show_registers(regs); +#ifdef CONFIG_BUG +extern void die_if_kernel(const char *str, struct pt_regs *regs, long err); -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif +/* Copy of the regs at BUG() time. */ +struct pt_regs BUG_regs; - do_exit(SIGSEGV); +void do_BUG(char *file, unsigned int line) +{ + printk("kernel BUG at %s:%d!\n", file, line); + die_if_kernel("Oops", &BUG_regs, 0); } +EXPORT_SYMBOL(do_BUG); -void arch_enable_nmi(void) +void fixup_BUG(struct pt_regs *regs) { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); + BUG_regs = *regs; + +#ifdef CONFIG_DEBUG_BUGVERBOSE + /* + * Fixup the BUG arguments through exception handlers. + */ + { + const struct exception_table_entry *fixup; + + /* + * ERP points at the "break 14" + 2, compensate for the 2 + * bytes. + */ + fixup = search_exception_tables(instruction_pointer(regs) - 2); + if (fixup) { + /* Adjust the instruction pointer in the stackframe. */ + instruction_pointer(regs) = fixup->fixup; + arch_fixup(regs); + } + } +#else + /* Dont try to lookup the filename + line, just dump regs. */ + do_BUG("unknown", 0); +#endif } + +/* + * Break 14 handler. Save regs and jump into the fixup_BUG. + */ +__asm__ ( ".text\n\t" + ".global breakh_BUG\n\t" + "breakh_BUG:\n\t" + SAVE_ALL + KGDB_FIXUP + "move.d $sp, $r10\n\t" + "jsr fixup_BUG\n\t" + "nop\n\t" + "jump ret_from_intr\n\t" + "nop\n\t"); + + +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) +{ +} +#endif +#endif diff --git a/arch/cris/arch-v32/kernel/vcs_hook.c b/arch/cris/arch-v32/kernel/vcs_hook.c deleted file mode 100644 index 64d71c54c22..00000000000 --- a/arch/cris/arch-v32/kernel/vcs_hook.c +++ /dev/null @@ -1,96 +0,0 @@ -// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook. This is the part running in the -// simulated program. -// - -#include "vcs_hook.h" -#include <stdarg.h> -#include <asm/arch-v32/hwregs/reg_map.h> -#include <asm/arch-v32/hwregs/intr_vect_defs.h> - -#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ -#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ - -#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0) -#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset] - - -// ------------------------------------------------------------------ hook_call -int hook_call( unsigned id, unsigned pcnt, ...) { - va_list ap; - unsigned i; - unsigned ret; -#ifdef USING_SOS - PREEMPT_OFF_SAVE(); -#endif - - // pass parameters - HOOK_DATA(0) = id; - - /* Have to make hook_print_str a special case since we call with a - parameter of byte type. Should perhaps be a separate - hook_call. */ - - if (id == hook_print_str) { - int i; - char *str; - - HOOK_DATA(1) = pcnt; - - va_start(ap, pcnt); - str = (char*)va_arg(ap,unsigned); - - for (i=0; i!=pcnt; i++) { - HOOK_DATA_BYTE(8+i) = str[i]; - } - HOOK_DATA_BYTE(8+i) = 0; /* null byte */ - } - else { - va_start(ap, pcnt); - for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned); - va_end(ap); - } - - // read from mem to make sure data has propagated to memory before trigging - *((volatile unsigned*) HOOK_MEM_BASE_ADDR); - - // trigger hook - HOOK_TRIG(id); - - // wait for call to finish - while( VHOOK_DATA(0) > 0 ) {} - - // extract return value - - ret = VHOOK_DATA(1); - -#ifdef USING_SOS - PREEMPT_RESTORE(); -#endif - return ret; -} - -unsigned -hook_buf(unsigned i) -{ - return (HOOK_DATA(i)); -} - -void print_str( const char *str ) { - int i; - for (i=1; str[i]; i++); /* find null at end of string */ - hook_call(hook_print_str, i, str); -} - -// --------------------------------------------------------------- CPU_KICK_DOG -void CPU_KICK_DOG(void) { - (void) hook_call( hook_kick_dog, 0 ); -} - -// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT -void CPU_WATCHDOG_TIMEOUT( unsigned t ) { - (void) hook_call( hook_dog_timeout, 1, t ); -} diff --git a/arch/cris/arch-v32/lib/Makefile b/arch/cris/arch-v32/lib/Makefile index 05b3ec6978d..eb4aad1f115 100644 --- a/arch/cris/arch-v32/lib/Makefile +++ b/arch/cris/arch-v32/lib/Makefile @@ -2,5 +2,6 @@ # Makefile for Etrax-specific library files.. # -lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o +lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o \ + csumcpfruser.o spinlock.o delay.o diff --git a/arch/cris/arch-v32/lib/checksum.S b/arch/cris/arch-v32/lib/checksum.S index 32e66181b82..87f3fd71ab1 100644 --- a/arch/cris/arch-v32/lib/checksum.S +++ b/arch/cris/arch-v32/lib/checksum.S @@ -1,6 +1,6 @@ /* * A fast checksum routine using movem - * Copyright (c) 1998-2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * csum_partial(const unsigned char * buff, int len, unsigned int sum) */ @@ -12,30 +12,23 @@ csum_partial: ;; r11 - length ;; r12 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r11 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r11 + blt _word_loop + move.d $r11, $acr subq 9*4,$sp - subq 10*4,$r11 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem checksum _mloop: movem [$r10+],$r9 ; read 10 longwords - + ;; Loop count without touching the c flag. + addoq -10*4, $acr, $acr ;; perform dword checksumming on the 10 longwords - add.d $r0,$r12 + addc $r0,$r12 addc $r1,$r12 addc $r2,$r12 addc $r3,$r12 @@ -46,60 +39,41 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r12 addc $r9,$r12 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r12 - addc 0,$r12 ; do it again, since we might have generated a carry - - subq 10*4,$r11 - bge _mloop - nop - - addq 10*4,$r11 ; compensate for last loop underflowing length + ;; test $acr without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r11 <= acr is not really needed in the mloop, just using the dslot + ;; to prepare for what is needed after mloop. + move.d $acr, $r11 + ;; fold the last carry into r13 + addc 0, $r12 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r12 - beq _no_fold - - ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below. - ;; r9 and r13 can be used as temporaries. + addq 10*4,$r11 ; compensate for last loop underflowing length moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9 lsrq 16,$r9 move.d $r12,$r13 lsrq 16,$r13 ; r13 = checksum >> 16 - and.d $r9,$r12 ; checksum = checksum & 0xffff - add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 + and.d $r9,$r12 ; checksum = checksum & 0xffff _no_fold: - cmpq 2,$r11 + subq 2,$r11 blt _no_words - nop + add.d $r13,$r12 ; checksum += r13 ;; checksum the rest of the words - - subq 2,$r11 - _wloop: subq 2,$r11 bge _wloop addu.w [$r10+],$r12 - addq 2,$r11 - _no_words: + addq 2,$r11 ;; see if we have one odd byte more - cmpq 1,$r11 - beq _do_byte + bne _do_byte nop ret move.d $r12,$r10 diff --git a/arch/cris/arch-v32/lib/checksumcopy.S b/arch/cris/arch-v32/lib/checksumcopy.S index 9303ccbadc6..21aabe91489 100644 --- a/arch/cris/arch-v32/lib/checksumcopy.S +++ b/arch/cris/arch-v32/lib/checksumcopy.S @@ -1,6 +1,6 @@ /* * A fast checksum+copy routine using movem - * Copyright (c) 1998, 2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * Authors: Bjorn Wesen * @@ -16,32 +16,23 @@ csum_partial_copy_nocheck: ;; r12 - length ;; r13 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r12 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r12 + blt _word_loop + move.d $r12, $acr subq 9*4,$sp - subq 10*4,$r12 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem copy and checksum - 1: ;; A failing userspace access (the read) will have this as PC. _mloop: movem [$r10+],$r9 ; read 10 longwords + addoq -10*4, $acr, $acr ; loop counter in latency cycle movem $r9,[$r11+] ; write 10 longwords ;; perform dword checksumming on the 10 longwords - - add.d $r0,$r13 + addc $r0,$r13 addc $r1,$r13 addc $r2,$r13 addc $r3,$r13 @@ -52,47 +43,30 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r13 addc $r9,$r13 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r13 - addc 0,$r13 ; do it again, since we might have generated a carry - - subq 10*4,$r12 - bge _mloop - nop - - addq 10*4,$r12 ; compensate for last loop underflowing length + ;; test $acr, without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r12 <= acr is needed after mloop and in the exception handlers. + move.d $acr, $r12 + ;; fold the last carry into r13 + addc 0, $r13 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r13 - beq _no_fold + addq 10*4,$r12 ; compensate for last loop underflowing length ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below ;; r9 can be used as temporary. - move.d $r13,$r9 lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff - add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 -_no_fold: - cmpq 2,$r12 + subq 2, $r12 blt _no_words - nop + add.d $r9,$r13 ; checksum += r0 ;; copy and checksum the rest of the words - - subq 2,$r12 - 2: ;; A failing userspace access for the read below will have this as PC. _wloop: move.w [$r10+],$r9 addu.w $r9,$r13 @@ -100,12 +74,9 @@ _wloop: move.w [$r10+],$r9 bge _wloop move.w $r9,[$r11+] - addq 2,$r12 - _no_words: - ;; see if we have one odd byte more - cmpq 1,$r12 - beq _do_byte + addq 2,$r12 + bne _do_byte nop ret move.d $r13,$r10 diff --git a/arch/cris/arch-v32/lib/delay.c b/arch/cris/arch-v32/lib/delay.c new file mode 100644 index 00000000000..39f1ac9995b --- /dev/null +++ b/arch/cris/arch-v32/lib/delay.c @@ -0,0 +1,28 @@ +/* + * Precise Delay Loops for ETRAX FS + * + * Copyright (C) 2006 Axis Communications AB. + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/module.h> + +/* + * On ETRAX FS, we can check the free-running read-only 100MHz timer + * getting 32-bit 10ns precision, theoretically good for 42.94967295 + * seconds. Unsigned arithmetic and careful expression handles + * wrapping. + */ + +void cris_delay10ns(u32 n10ns) +{ + u32 t0 = REG_RD(timer, regi_timer0, r_time); + while (REG_RD(timer, regi_timer0, r_time) - t0 < n10ns) + ; +} +EXPORT_SYMBOL(cris_delay10ns); diff --git a/arch/cris/arch-v32/lib/spinlock.S b/arch/cris/arch-v32/lib/spinlock.S index 2437ae7f6ed..79087ef59a1 100644 --- a/arch/cris/arch-v32/lib/spinlock.S +++ b/arch/cris/arch-v32/lib/spinlock.S @@ -12,11 +12,11 @@ cris_spin_lock: clearf p -1: test.d [$r10] +1: test.b [$r10] beq 1b clearf p ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret @@ -24,10 +24,10 @@ cris_spin_lock: cris_spin_trylock: clearf p -1: move.d [$r10], $r11 +1: move.b [$r10], $r11 ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret - move.d $r11,$r10 + movu.b $r11,$r10 diff --git a/arch/cris/arch-v32/mach-a3/Kconfig b/arch/cris/arch-v32/mach-a3/Kconfig new file mode 100644 index 00000000000..a4df06d5997 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Kconfig @@ -0,0 +1,110 @@ +if CRIS_MACH_ARTPEC3 + +menu "Artpec-3 options" + depends on CRIS_MACH_ARTPEC3 + +config ETRAX_DRAM_VIRTUAL_BASE + hex + default "c0000000" + +config ETRAX_L2CACHE + bool + default y + +config ETRAX_SERIAL_PORTS + int + default 5 + +config ETRAX_DDR + bool + default y + +config ETRAX_DDR2_MRS + hex "DDR2 MRS" + default "0" + +config ETRAX_DDR2_TIMING + hex "DDR2 SDRAM timing" + default "0" + help + SDRAM timing parameters. + +config ETRAX_DDR2_CONFIG + hex "DDR2 config" + default "0" + +config ETRAX_PIO_CE0_CFG + hex "PIO CE0 configuration" + default "0" + +config ETRAX_PIO_CE1_CFG + hex "PIO CE1 configuration" + default "0" + +config ETRAX_PIO_CE2_CFG + hex "PIO CE2 configuration" + default "0" + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + default "00000000" + 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" + default "00000000" + 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" + default "000000000" + 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" + default "000000000" + 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" + 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" + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-a3/Makefile b/arch/cris/arch-v32/mach-a3/Makefile new file mode 100644 index 00000000000..41fa6a6893a --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +obj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-a3/arbiter.c b/arch/cris/arch-v32/mach-a3/arbiter.c new file mode 100644 index 00000000000..8b924db71c9 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/arbiter.c @@ -0,0 +1,634 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + * + * The artpec-3 has two arbiters. The memory hierarchy looks like this: + * + * + * CPU DMAs + * | | + * | | + * -------------- ------------------ + * | foo arbiter|----| Internal memory| + * -------------- ------------------ + * | + * -------------- + * | L2 cache | + * -------------- + * | + * h264 etc | + * | | + * | | + * -------------- + * | bar arbiter| + * -------------- + * | + * --------- + * | SDRAM | + * --------- + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_foo_defs.h> +#include <hwregs/marb_bar_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +#define D(x) + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define SDRAM_BANDWIDTH 400000000 +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_SLOTS 64 +#define NBR_OF_REGIONS 2 +#define NBR_OF_CLIENTS 15 +#define ARBITERS 2 +#define UNASSIGNED 100 + +struct arbiter { + unsigned long instance; + int nbr_regions; + int nbr_clients; + int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; + int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +}; + +static struct crisv32_watch_entry watches[ARBITERS][NUMBER_OF_BP] = +{ + { + {regi_marb_foo_bp0}, + {regi_marb_foo_bp1}, + {regi_marb_foo_bp2}, + {regi_marb_foo_bp3} + }, + { + {regi_marb_bar_bp0}, + {regi_marb_bar_bp1}, + {regi_marb_bar_bp2}, + {regi_marb_bar_bp3} + } +}; + +struct arbiter arbiters[ARBITERS] = +{ + { /* L2 cache arbiter */ + .instance = regi_marb_foo, + .nbr_regions = 2, + .nbr_clients = 15 + }, + { /* DDR2 arbiter */ + .instance = regi_marb_bar, + .nbr_regions = 1, + .nbr_clients = 9 + } +}; + +static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id); +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int arbiter, int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < arbiters[arbiter].nbr_clients; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!arbiters[arbiter].requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!arbiters[arbiter].active_clients[region][client] || + unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = NBR_OF_SLOTS / + arbiters[arbiter].requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!arbiters[arbiter].active_clients[region][client]) { + client = (client + 1) % + arbiters[arbiter].nbr_clients; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % arbiters[arbiter].nbr_clients; + } + if (arbiter == 0) { + if (region == EXT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_l2_slots, slot, val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_intm_slots, slot, val[slot]); + } else { + REG_WR_INT_VECT(marb_bar, regi_marb_bar, + rw_ddr2_slots, slot, val[slot]); + } + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + arbiters[0].active_clients[EXT_REGION][11] = 1; + arbiters[0].active_clients[EXT_REGION][12] = 1; + crisv32_arbiter_config(0, EXT_REGION, 0); + crisv32_arbiter_config(0, INT_REGION, 0); + crisv32_arbiter_config(1, EXT_REGION, 0); + + if (request_irq(MEMARB_FOO_INTR_VECT, crisv32_foo_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + + if (request_irq(MEMARB_BAR_INTR_VECT, crisv32_bar_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + MARB_CLIENTS(arbiter_all_clients, arbiter_bar_all_clients), + arbiter_all_write, NULL); +#endif + + /* Set up max burst sizes by default */ + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_ccd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vout_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_fifo_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_l2cache_burst, 3); +} + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + int arbiter = 0; + + crisv32_arbiter_init(); + + if (client & 0xffff0000) { + arbiter = 1; + client >>= 16; + } + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) { + total_assigned += arbiters[arbiter].requested_slots[region][i]; + total_clients += arbiters[arbiter].active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + arbiters[arbiter].active_clients[region][client] = 1; + arbiters[arbiter].requested_slots[region][client] = req; + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); + + /* Propagate allocation from foo to bar */ + if (arbiter == 0) + crisv32_arbiter_allocate_bandwidth(8 << 16, + EXT_REGION, bandwidth); + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + int arbiter = 0; + + if (client & 0xffff0000) + arbiter = 1; + + arbiters[arbiter].requested_slots[region][client] = 0; + arbiters[arbiter].active_clients[region][client] = 0; + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) + total_assigned += arbiters[arbiter].requested_slots[region][i]; + + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + int arbiter; + int used[2]; + int ret = 0; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + if (clients & 0xffff) + used[0] = 1; + if (clients & 0xffff0000) + used[1] = 1; + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + if (!used[arbiter]) + continue; + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[arbiter][i].used) { + unsigned intr_mask; + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, + regi_marb_bar, rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, + regi_marb_foo, rw_intr_mask); + + watches[arbiter][i].used = 1; + watches[arbiter][i].start = start; + watches[arbiter][i].end = start + size; + watches[arbiter][i].cb = cb; + + ret |= (i + 1) << (arbiter + 8); + if (arbiter) { + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_clients, + clients & 0xffff); + } else { + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_clients, clients >> 16); + } + + if (i == 0) + intr_mask |= 1; + else if (i == 1) + intr_mask |= 2; + else if (i == 2) + intr_mask |= 4; + else if (i == 3) + intr_mask |= 8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, + rw_intr_mask, intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, + rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + + break; + } + } + } + spin_unlock(&arbiter_lock); + if (ret) + return ret; + else + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + int arbiter; + int intr_mask; + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + int id2; + + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, regi_marb_bar, + rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, regi_marb_foo, + rw_intr_mask); + + id2 = (id & (0xff << (arbiter + 8))) >> (arbiter + 8); + if (id2 == 0) + continue; + id2--; + if ((id2 >= NUMBER_OF_BP) || (!watches[arbiter][id2].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[arbiter][id2], 0, + sizeof(struct crisv32_watch_entry)); + + if (id2 == 0) + intr_mask &= ~1; + else if (id2 == 1) + intr_mask &= ~2; + else if (id2 == 2) + intr_mask &= ~4; + else if (id2 == 3) + intr_mask &= ~8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, rw_intr_mask, + intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, rw_intr_mask, + intr_mask); + } + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_foo_r_masked_intr masked_intr = + REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + reg_marb_foo_bp_r_brk_clients r_clients; + reg_marb_foo_bp_r_brk_addr r_addr; + reg_marb_foo_bp_r_brk_op r_op; + reg_marb_foo_bp_r_brk_first_client r_first; + reg_marb_foo_bp_r_brk_size r_size; + reg_marb_foo_bp_rw_ack ack = {0}; + reg_marb_foo_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_foo_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_foo_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_foo_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_foo_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_foo_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_size, r_size)); + + REG_WR(marb_foo_bp, watch->instance, rw_ack, ack); + REG_WR(marb_foo, regi_marb_foo, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_bar_r_masked_intr masked_intr = + REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + reg_marb_bar_bp_r_brk_clients r_clients; + reg_marb_bar_bp_r_brk_addr r_addr; + reg_marb_bar_bp_r_brk_op r_op; + reg_marb_bar_bp_r_brk_first_client r_first; + reg_marb_bar_bp_r_brk_size r_size; + reg_marb_bar_bp_rw_ack ack = {0}; + reg_marb_bar_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bar_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bar_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bar_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bar_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bar_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_size, r_size)); + + REG_WR(marb_bar_bp, watch->instance, rw_ack, ack); + REG_WR(marb_bar, regi_marb_bar, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + diff --git a/arch/cris/arch-v32/mach-a3/cpufreq.c b/arch/cris/arch-v32/mach-a3/cpufreq.c new file mode 100644 index 00000000000..8e5a3cab8ad --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/cpufreq.c @@ -0,0 +1,153 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/ddr2_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i = 0; + struct cpufreq_freqs freqs; + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + +#ifdef CONFIG_SMP + for_each_present_cpu(i) +#endif + { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target(policy, cris_freq_table, + target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_ddr2_rw_cfg cfg = + REG_RD(ddr2, regi_ddr2_ctrl, rw_cfg); + cfg.ref_interval = (freqs->new == 200000 ? 1560 : 46); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++); + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/mach-a3/dma.c b/arch/cris/arch-v32/mach-a3/dma.c new file mode 100644 index 00000000000..25f236ef0b8 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dma.c @@ -0,0 +1,185 @@ +/* Wrapper for DMA channel allocator that starts clocks etc */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <asm/arch/mach/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/strmux_defs.h> +#include <linux/errno.h> +#include <asm/system.h> +#include <arbiter.h> + +static char used_dma_channels[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; + +static DEFINE_SPINLOCK(dma_lock); + +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) +{ + unsigned long flags; + reg_clkgen_rw_clk_ctrl clk_ctrl; + reg_strmux_rw_cfg strmux_cfg; + + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; + + spin_lock_irqsave(&dma_lock, flags); + + if (used_dma_channels[dmanr]) { + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); + return -EBUSY; + } + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); + + switch (dmanr) { + case 0: + case 1: + clk_ctrl.dma0_1_eth = 1; + break; + case 2: + case 3: + clk_ctrl.dma2_3_strcop = 1; + break; + case 4: + case 5: + clk_ctrl.dma4_5_iop = 1; + break; + case 6: + case 7: + clk_ctrl.sser_ser_dma6_7 = 1; + break; + case 9: + case 11: + clk_ctrl.dma9_11 = 1; + break; +#if MAX_DMA_CHANNELS-1 != 11 +#error Check dma.c +#endif + default: + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS-1); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + return -EINVAL; + } + + switch (owner) { + case dma_eth: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_eth; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_eth; + else + panic("Invalid DMA channel for eth\n"); + break; + case dma_ser0: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_ser0; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_ser0; + else + panic("Invalid DMA channel for ser0\n"); + break; + case dma_ser3: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ser3; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ser3; + else + panic("Invalid DMA channel for ser3\n"); + break; + case dma_strp: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_strcop; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_strcop; + else + panic("Invalid DMA channel for strp\n"); + break; + case dma_ser1: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_ser1; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_ser1; + else + panic("Invalid DMA channel for ser1\n"); + break; + case dma_iop: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_iop; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_iop; + else + panic("Invalid DMA channel for iop\n"); + break; + case dma_ser2: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_ser2; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_ser2; + else + panic("Invalid DMA channel for ser2\n"); + break; + case dma_sser: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_sser; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_sser; + else + panic("Invalid DMA channel for sser\n"); + break; + case dma_ser4: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_ser4; + else + panic("Invalid DMA channel for ser4\n"); + break; + case dma_jpeg: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_jpeg; + else + panic("Invalid DMA channel for JPEG\n"); + break; + case dma_h264: + if (dmanr == 11) + strmux_cfg.dma11 = regk_strmux_h264; + else + panic("Invalid DMA channel for H264\n"); + break; + } + + used_dma_channels[dmanr] = 1; + used_dma_channels_users[dmanr] = device_id; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); + spin_unlock_irqrestore(&dma_lock, flags); + return 0; +} + +void crisv32_free_dma(unsigned int dmanr) +{ + spin_lock(&dma_lock); + used_dma_channels[dmanr] = 0; + spin_unlock(&dma_lock); +} diff --git a/arch/cris/arch-v32/mach-a3/dram_init.S b/arch/cris/arch-v32/mach-a3/dram_init.S new file mode 100644 index 00000000000..94d6b41cb29 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dram_init.S @@ -0,0 +1,104 @@ +/* + * DDR SDRAM initialization - alter with care + * This file is intended to be included from other assembler files + * + * Note: This file may not modify r8 or r9 because they are used to + * carry information from the decompresser to the kernel + * + * Copyright (C) 2005-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +/* Just to be certain the config file is included, we include it here + * explicitely instead of depending on it being included in the file that + * uses this code. + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> + + ;; WARNING! The registers r8 and r9 are used as parameters carrying + ;; information from the decompressor (if the kernel was compressed). + ;; They should not be used in the code below. + + ;; Refer to ddr2 MDS for initialization sequence + + ; Start clock + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_cfg), $r0 + move.d REG_STATE(ddr2, rw_phy_cfg, en, yes), $r1 + move.d $r1, [$r0] + + ; Reset phy and start calibration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_ctrl), $r0 + move.d REG_STATE(ddr2, rw_phy_ctrl, rst, yes) | \ + REG_STATE(ddr2, rw_phy_ctrl, cal_rst, yes), $r1 + move.d $r1, [$r0] + move.d REG_STATE(ddr2, rw_phy_ctrl, cal_start, yes), $r1 + move.d $r1, [$r0] + + ; 2. Wait 200us + move.d 10000, $r2 +1: bne 1b + subq 1, $r2 + + ; Issue commands + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_ctrl), $r0 + move.d sdram_commands_start, $r2 +command_loop: + movu.b [$r2+], $r1 + movu.w [$r2+], $r3 +do_cmd: + lslq 16, $r1 + or.d $r3, $r1 + move.d $r1, [$r0] + cmp.d sdram_commands_end, $r2 + blo command_loop + nop + + ; Set timing + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing), $r0 + move.d CONFIG_ETRAX_DDR2_TIMING, $r1 + move.d $r1, [$r0] + + ; Set latency + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_latency), $r0 + move.d 0x13, $r1 + move.d $r1, [$r0] + + ; Set configuration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg), $r0 + move.d CONFIG_ETRAX_DDR2_CONFIG, $r1 + move.d $r1, [$r0] + + ba after_sdram_commands + nop + +sdram_commands_start: + .byte regk_ddr2_deselect + .word 0 + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_emrs2 + .word 0 + .byte regk_ddr2_emrs3 + .word 0 + .byte regk_ddr2_emrs + .word regk_ddr2_dll_en + .byte regk_ddr2_mrs + .word regk_ddr2_dll_rst + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_mrs + .word CONFIG_ETRAX_DDR2_MRS & 0xffff + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_default | regk_ddr2_dll_en + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_exit | regk_ddr2_dll_en | (CONFIG_ETRAX_DDR2_MRS >> 16) +sdram_commands_end: + .align 1 +after_sdram_commands: diff --git a/arch/cris/arch-v32/mach-a3/hw_settings.S b/arch/cris/arch-v32/mach-a3/hw_settings.S new file mode 100644 index 00000000000..258a6329cd4 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/hw_settings.S @@ -0,0 +1,51 @@ +/* + * This table is used by some tools to extract hardware parameters. + * The table should be included in the kernel and the decompressor. + * Don't forget to update the tools if you change this table. + * + * Copyright (C) 2001-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> + + .ascii "HW_PARAM_MAGIC" ; Magic number + .dword 0xc0004000 ; Kernel start address + + ; Debug port +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + .dword 0 +#elif defined(CONFIG_ETRAX_DEBUG_PORT1) + .dword 1 +#elif defined(CONFIG_ETRAX_DEBUG_PORT2) + .dword 2 +#elif defined(CONFIG_ETRAX_DEBUG_PORT3) + .dword 3 +#else + .dword 4 ; No debug +#endif + + ; Register values + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg) + .dword CONFIG_ETRAX_DDR2_CONFIG + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing) + .dword CONFIG_ETRAX_DDR2_TIMING + .dword CONFIG_ETRAX_DDR2_MRS + + .dword REG_ADDR(gio, regi_gio, rw_pa_dout) + .dword CONFIG_ETRAX_DEF_GIO_PA_OUT + .dword REG_ADDR(gio, regi_gio, rw_pa_oe) + .dword CONFIG_ETRAX_DEF_GIO_PA_OE + .dword REG_ADDR(gio, regi_gio, rw_pb_dout) + .dword CONFIG_ETRAX_DEF_GIO_PB_OUT + .dword REG_ADDR(gio, regi_gio, rw_pb_oe) + .dword CONFIG_ETRAX_DEF_GIO_PB_OE + .dword REG_ADDR(gio, regi_gio, rw_pc_dout) + .dword CONFIG_ETRAX_DEF_GIO_PC_OUT + .dword REG_ADDR(gio, regi_gio, rw_pc_oe) + .dword CONFIG_ETRAX_DEF_GIO_PC_OE + + .dword 0 ; No more register values diff --git a/arch/cris/arch-v32/mach-a3/io.c b/arch/cris/arch-v32/mach-a3/io.c new file mode 100644 index 00000000000..9eeaf3eca47 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/io.c @@ -0,0 +1,149 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/mach/pinmux.h> +#include <hwregs/gio_defs.h> + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 16 + }, +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 32 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif + diff --git a/arch/cris/arch-v32/mach-a3/pinmux.c b/arch/cris/arch-v32/mach-a3/pinmux.c new file mode 100644 index 00000000000..0a28c9bedfb --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/pinmux.c @@ -0,0 +1,386 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> +#include <hwregs/clkgen_defs.h> + +#undef DEBUG + +#define PINS 80 +#define PORT_PINS 32 +#define PORTS 3 + +static char pins[PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int +crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + crisv32_pinmux_alloc(PORT_A, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_B, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port >= PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port * PORT_PINS + i] != pinmux_none) && + (pins[port * PORT_PINS + i] != pinmux_gpio) && + (pins[port * PORT_PINS + i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen, + rw_clk_ctrl); + + switch (function) { + case pinmux_eth: + clk_ctrl.eth = regk_clkgen_yes; + clk_ctrl.dma0_1_eth = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 8, 23, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_B, 24, 25, pinmux_fixed); + hwprot.eth = hwprot.eth_mdio = regk_pinmux_yes; + break; + case pinmux_geth: + ret = crisv32_pinmux_alloc(PORT_B, 0, 7, pinmux_fixed); + hwprot.geth = regk_pinmux_yes; + break; + case pinmux_tg_cmos: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 29, pinmux_fixed); + hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_tg_ccd: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 31, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_fixed); + hwprot.tg = hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_vout: + clk_ctrl.strdma0_2_video = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 8, 18, pinmux_fixed); + hwprot.vout = hwprot.vout_sync = regk_pinmux_yes; + break; + case pinmux_ser1: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 24, 25, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 26, 27, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 28, 29, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_ser4: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 30, 31, pinmux_fixed); + hwprot.ser4 = regk_pinmux_yes; + break; + case pinmux_sser: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 19, 23, pinmux_fixed); + hwprot.sser = regk_pinmux_yes; + break; + case pinmux_pio: + hwprot.pio = regk_pinmux_yes; + ret = 0; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_alloc(PORT_A, 30, 30, pinmux_fixed); + hwprot.pwm0 = regk_pinmux_yes; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_alloc(PORT_A, 31, 31, pinmux_fixed); + hwprot.pwm1 = regk_pinmux_yes; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_alloc(PORT_B, 26, 26, pinmux_fixed); + hwprot.pwm2 = regk_pinmux_yes; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_alloc(PORT_A, 0, 1, pinmux_fixed); + hwprot.i2c0 = regk_pinmux_yes; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + hwprot.i2c1 = regk_pinmux_yes; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 7, 7, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_yes; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 4, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda1 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 5, 5, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda2 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 6, 6, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda3 = regk_pinmux_yes; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + } else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + int pin = port * PORT_PINS; + + for (i = 0; (i < PORT_PINS) && (pin < PINS); i++, pin++) { + if (pins[pin] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[pin] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_gio_pa + 4 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_iop_pa + 4 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int +crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_eth: + ret = crisv32_pinmux_dealloc(PORT_B, 8, 23); + ret |= crisv32_pinmux_dealloc(PORT_B, 24, 25); + ret |= crisv32_pinmux_dealloc(PORT_B, 0, 7); + hwprot.eth = hwprot.eth_mdio = hwprot.geth = regk_pinmux_no; + break; + case pinmux_tg_cmos: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 29); + hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_tg_ccd: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 31); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 15); + hwprot.tg = hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_vout: + ret = crisv32_pinmux_dealloc(PORT_A, 8, 18); + hwprot.vout = hwprot.vout_sync = regk_pinmux_no; + break; + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_A, 24, 25); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_A, 26, 27); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_A, 28, 29); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_ser4: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 31); + hwprot.ser4 = regk_pinmux_no; + break; + case pinmux_sser: + ret = crisv32_pinmux_dealloc(PORT_A, 19, 23); + hwprot.sser = regk_pinmux_no; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 30); + hwprot.pwm0 = regk_pinmux_no; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_dealloc(PORT_A, 31, 31); + hwprot.pwm1 = regk_pinmux_no; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_dealloc(PORT_B, 26, 26); + hwprot.pwm2 = regk_pinmux_no; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_dealloc(PORT_A, 0, 1); + hwprot.i2c0 = regk_pinmux_no; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + hwprot.i2c1 = regk_pinmux_no; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 7, 7); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_no; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 4); + hwprot.i2c1_sda1 = regk_pinmux_no; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 5, 5); + hwprot.i2c1_sda2 = regk_pinmux_no; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 6, 6); + hwprot.i2c1_sda3 = regk_pinmux_no; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_dump(void) +{ + int i, j; + int pin = 0; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + pin++; + printk(KERN_DEBUG "Port %c\n", 'A'+i); + for (j = 0; (j < PORT_PINS) && (pin < PINS); j++, pin++) + printk(KERN_DEBUG + " Pin %d = %d\n", j, pins[i * PORT_PINS + j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.c b/arch/cris/arch-v32/mach-a3/vcs_hook.c new file mode 100644 index 00000000000..58b1a5469fd --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.c @@ -0,0 +1,103 @@ +/* + * Simulator hook mechanism + */ + +#include "vcs_hook.h" +#include <asm/io.h> +#include <stdarg.h> + +#define HOOK_TRIG_ADDR 0xb7000000 +#define HOOK_MEM_BASE_ADDR 0xce000000 + +static volatile unsigned *hook_base; + +#define HOOK_DATA(offset) hook_base[offset] +#define VHOOK_DATA(offset) hook_base[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)hook_base)[offset] + +static void hook_init(void) +{ + static int first = 1; + if (first) { + first = 0; + hook_base = ioremap(HOOK_MEM_BASE_ADDR, 8192); + } +} + +static unsigned hook_trig(unsigned id) +{ + unsigned ret; + + /* preempt_disable(); */ + + /* Dummy read from mem to make sure data has propagated to memory + * before trigging */ + ret = *hook_base; + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + + return ret; +} + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + + ret = hook_trig(id); + + return ret; +} + +int hook_call_str(unsigned id, unsigned size, const char *str) +{ + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + HOOK_DATA(1) = size; + + for (i = 0; i < size; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + HOOK_DATA_BYTE(8 + i) = 0; + + ret = hook_trig(id); + + return ret; +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ +} diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.h b/arch/cris/arch-v32/mach-a3/vcs_hook.h new file mode 100644 index 00000000000..8b73d0e8392 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.h @@ -0,0 +1,58 @@ +/* + * Simulator hook call mechanism + */ + +#ifndef __hook_h__ +#define __hook_h__ + +int hook_call(unsigned id, unsigned pcnt, ...); +int hook_call_str(unsigned id, unsigned size, const char *str); + +enum hook_ids { + hook_debug_on = 1, + hook_debug_off, + hook_stop_sim_ok, + hook_stop_sim_fail, + hook_alloc_shared, + hook_ptr_shared, + hook_free_shared, + hook_file2shared, + hook_cmp_shared, + hook_print_params, + hook_sim_time, + hook_stop_sim, + hook_kick_dog, + hook_dog_timeout, + hook_rand, + hook_srand, + hook_rand_range, + hook_print_str, + hook_print_hex, + hook_cmp_offset_shared, + hook_fill_random_shared, + hook_alloc_random_data, + hook_calloc_random_data, + hook_print_int, + hook_print_uint, + hook_fputc, + hook_init_fd, + hook_sbrk, + hook_print_context_descr, + hook_print_data_descr, + hook_print_group_descr, + hook_fill_shared, + hook_sl_srand, + hook_sl_rand_irange, + hook_sl_rand_urange, + hook_sl_sh_malloc_aligned, + hook_sl_sh_calloc_aligned, + hook_sl_sh_alloc_random_data, + hook_sl_sh_file2mem, + hook_sl_vera_mbox_handle, + hook_sl_vera_mbox_put, + hook_sl_vera_mbox_get, + hook_sl_system, + hook_sl_sh_hexdump +}; + +#endif diff --git a/arch/cris/arch-v32/mach-fs/Kconfig b/arch/cris/arch-v32/mach-fs/Kconfig new file mode 100644 index 00000000000..f6d74475f1c --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Kconfig @@ -0,0 +1,216 @@ +if ETRAXFS + +menu "ETRAX FS options" + depends on ETRAXFS + +config ETRAX_DRAM_VIRTUAL_BASE + hex + depends on ETRAX_ARCH_V32 + default "c0000000" + +config ETRAX_SERIAL_PORTS + int + default 4 + +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. + +config ETRAX_DEF_GIO_PV_OE + hex "GIO_PV_OE" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the direction of virtual general port V bits. 1 is out, + 0 is in. This is often totally different depending on the product + used. These bits are used for all kinds of stuff. If you don't know + what to use, it is always safe to put all as inputs, although + floating inputs isn't good. + +config ETRAX_DEF_GIO_PV_OUT + hex "GIO_PV_OUT" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the initial data for the virtual general port V bits. + Most products should use 0000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-fs/Makefile b/arch/cris/arch-v32/mach-fs/Makefile new file mode 100644 index 00000000000..4ff407a1b93 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +bj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-fs/arbiter.c b/arch/cris/arch-v32/mach-fs/arbiter.c new file mode 100644 index 00000000000..84d31bd7b69 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/arbiter.c @@ -0,0 +1,404 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define NBR_OF_CLIENTS 14 +#define NBR_OF_SLOTS 64 +#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_REGIONS 2 + +static struct crisv32_watch_entry watches[NUMBER_OF_BP] = { + {regi_marb_bp0}, + {regi_marb_bp1}, + {regi_marb_bp2}, + {regi_marb_bp3} +}; + +static u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static u8 active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static int max_bandwidth[NBR_OF_REGIONS] = + { SDRAM_BANDWIDTH, INTMEM_BANDWIDTH }; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < NBR_OF_CLIENTS; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!active_clients[region][client] + || unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = + NBR_OF_SLOTS / requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!active_clients[region][client]) { + client = (client + 1) % NBR_OF_CLIENTS; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % NBR_OF_CLIENTS; + } + if (region == EXT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, + val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, + val[slot]); + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; + crisv32_arbiter_config(EXT_REGION, 0); + crisv32_arbiter_config(INT_REGION, 0); + + if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, + "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + arbiter_all_clients, arbiter_all_write, NULL); +#endif +} + +/* Main entry for bandwidth allocation. */ + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + + crisv32_arbiter_init(); + + for (i = 0; i < NBR_OF_CLIENTS; i++) { + total_assigned += requested_slots[region][i]; + total_clients += active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + active_clients[region][client] = 1; + requested_slots[region][client] = req; + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); + + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + + requested_slots[region][client] = 0; + active_clients[region][client] = 0; + + for (i = 0; i < NBR_OF_CLIENTS; i++) + total_assigned += requested_slots[region][i]; + + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[i].used) { + reg_marb_rw_intr_mask intr_mask = + REG_RD(marb, regi_marb, rw_intr_mask); + + watches[i].used = 1; + watches[i].start = start; + watches[i].end = start + size; + watches[i].cb = cb; + + REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, + watches[i].start); + REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, + watches[i].end); + REG_WR_INT(marb_bp, watches[i].instance, rw_op, + accesses); + REG_WR_INT(marb_bp, watches[i].instance, rw_clients, + clients); + + if (i == 0) + intr_mask.bp0 = regk_marb_yes; + else if (i == 1) + intr_mask.bp1 = regk_marb_yes; + else if (i == 2) + intr_mask.bp2 = regk_marb_yes; + else if (i == 3) + intr_mask.bp3 = regk_marb_yes; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + spin_unlock(&arbiter_lock); + + return i; + } + } + spin_unlock(&arbiter_lock); + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); + + if (id == 0) + intr_mask.bp0 = regk_marb_no; + else if (id == 1) + intr_mask.bp2 = regk_marb_no; + else if (id == 2) + intr_mask.bp2 = regk_marb_no; + else if (id == 3) + intr_mask.bp3 = regk_marb_no; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_r_masked_intr masked_intr = + REG_RD(marb, regi_marb, r_masked_intr); + reg_marb_bp_r_brk_clients r_clients; + reg_marb_bp_r_brk_addr r_addr; + reg_marb_bp_r_brk_op r_op; + reg_marb_bp_r_brk_first_client r_first; + reg_marb_bp_r_brk_size r_size; + reg_marb_bp_rw_ack ack = { 0 }; + reg_marb_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + + if (masked_intr.bp0) { + watch = &watches[0]; + ack_intr.bp0 = regk_marb_yes; + } else if (masked_intr.bp1) { + watch = &watches[1]; + ack_intr.bp1 = regk_marb_yes; + } else if (masked_intr.bp2) { + watch = &watches[2]; + ack_intr.bp2 = regk_marb_yes; + } else if (masked_intr.bp3) { + watch = &watches[3]; + ack_intr.bp3 = regk_marb_yes; + } else { + return IRQ_NONE; + } + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bp, watch->instance, r_brk_size); + + printk(KERN_INFO "Arbiter IRQ\n"); + printk(KERN_INFO "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); + + REG_WR(marb_bp, watch->instance, rw_ack, ack); + REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); + + printk(KERN_INFO "IRQ occured at %lX\n", get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} diff --git a/arch/cris/arch-v32/mach-fs/cpufreq.c b/arch/cris/arch-v32/mach-fs/cpufreq.c new file mode 100644 index 00000000000..d57631c0d8d --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/cpufreq.c @@ -0,0 +1,146 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <asm/arch/hwregs/reg_rdwr.h> +#include <asm/arch/hwregs/config_defs.h> +#include <asm/arch/hwregs/bif_core_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i; + struct cpufreq_freqs freqs; + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + + for_each_possible_cpu(i) { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target + (policy, cris_freq_table, target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_bif_core_rw_sdram_timing timing = + REG_RD(bif_core, regi_bif_core, rw_sdram_timing); + timing.cpd = (freqs->new == 200000 ? 0 : 1); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++) ; + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/kernel/dma.c b/arch/cris/arch-v32/mach-fs/dma.c index 570e19128ff..a6acf4e6345 100644 --- a/arch/cris/arch-v32/kernel/dma.c +++ b/arch/cris/arch-v32/mach-fs/dma.c @@ -3,49 +3,54 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <asm/dma.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/hwregs/config_defs.h> -#include <asm/arch/hwregs/strmux_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/config_defs.h> +#include <hwregs/strmux_defs.h> #include <linux/errno.h> #include <asm/system.h> -#include <asm/arch/arbiter.h> +#include <asm/arch/mach/arbiter.h> static char used_dma_channels[MAX_DMA_CHANNELS]; -static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; static DEFINE_SPINLOCK(dma_lock); -int crisv32_request_dma(unsigned int dmanr, const char * device_id, - unsigned options, unsigned int bandwidth, +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) { unsigned long flags; reg_config_rw_clk_ctrl clk_ctrl; reg_strmux_rw_cfg strmux_cfg; - if (crisv32_arbiter_allocate_bandwidth(dmanr, - options & DMA_INT_MEM ? INT_REGION : EXT_REGION, - bandwidth)) - return -ENOMEM; + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? + INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; spin_lock_irqsave(&dma_lock, flags); if (used_dma_channels[dmanr]) { spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); } if (options & DMA_PANIC_ON_ERROR) panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); return -EBUSY; } clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); - switch(dmanr) - { + switch (dmanr) { case 0: case 1: clk_ctrl.dma01_eth0 = 1; @@ -72,7 +77,9 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, default: spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS - 1); } if (options & DMA_PANIC_ON_ERROR) @@ -80,8 +87,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, return -EINVAL; } - switch(owner) - { + switch (owner) { case dma_eth0: if (dmanr == 0) strmux_cfg.dma0 = regk_strmux_eth0; @@ -212,7 +218,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, used_dma_channels_users[dmanr] = device_id; REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); - spin_unlock_irqrestore(&dma_lock,flags); + spin_unlock_irqrestore(&dma_lock, flags); return 0; } diff --git a/arch/cris/arch-v32/lib/dram_init.S b/arch/cris/arch-v32/mach-fs/dram_init.S index 218fbe259ee..6fbad336527 100644 --- a/arch/cris/arch-v32/lib/dram_init.S +++ b/arch/cris/arch-v32/mach-fs/dram_init.S @@ -1,23 +1,22 @@ -/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $ - * +/* * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * * Note: This file may not modify r8 or r9 because they are used to * carry information from the decompresser to the kernel * - * Copyright (C) 2000-2003 Axis Communications AB + * Copyright (C) 2000-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ /* Just to be certain the config file is included, we include it here - * explicitly instead of depending on it being included in the file that + * explicitely instead of depending on it being included in the file that * uses this code. */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> ;; WARNING! The registers r8 and r9 are used as parameters carrying ;; information from the decompressor (if the kernel was compressed). @@ -46,7 +45,7 @@ move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2 move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 - and.d 0x07, $r1 ; Get CAS latency + and.d 0x07, $r1 ; Get CAS latency cmpq 2, $r1 ; CL = 2 ? beq _bw_check nop @@ -80,12 +79,10 @@ _set_timing: subq 1, $r2 ; Issue initialization command sequence - move.d _sdram_commands_start, $r2 - and.d 0x000fffff, $r2 ; Make sure commands are read from flash - move.d _sdram_commands_end, $r3 - and.d 0x000fffff, $r3 + lapc _sdram_commands_start, $r2 + lapc _sdram_commands_end, $r3 1: clear.d $r6 - move.b [$r2+], $r6 ; Load command + move.b [$r2+], $r6 ; Load command or.d $r4, $r6 ; Add calculated mrs move.d $r6, [$r5] ; Write rw_sdram_cmd ; Wait 80 ns between each command diff --git a/arch/cris/arch-v32/lib/hw_settings.S b/arch/cris/arch-v32/mach-fs/hw_settings.S index fff9443513d..8bde93c3621 100644 --- a/arch/cris/arch-v32/lib/hw_settings.S +++ b/arch/cris/arch-v32/mach-fs/hw_settings.S @@ -1,18 +1,16 @@ /* - * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $ - * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. * Don't forget to update the tools if you change this table. * - * Copyright (C) 2001 Axis Communications AB + * Copyright (C) 2001-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> -#include <asm/arch/hwregs/asm/gio_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> .ascii "HW_PARAM_MAGIC" ; Magic number .dword 0xc0004000 ; Kernel start address diff --git a/arch/cris/arch-v32/mach-fs/io.c b/arch/cris/arch-v32/mach-fs/io.c new file mode 100644 index 00000000000..a03a3ad3a18 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/io.c @@ -0,0 +1,191 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/hwregs/gio_defs.h> + +#ifndef DEBUG +#define DEBUG(x) +#endif + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 8 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pd_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pe_din), + 18 + } +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led_net1_green; +struct crisv32_iopin crisv32_led_net1_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 18 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += + crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += + crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + +#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO + ret += + crisv32_io_get_name(&crisv32_led_net1_green, + CONFIG_ETRAX_LED_G_NET1); + crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) { + crisv32_io_get_name(&crisv32_led_net1_red, + CONFIG_ETRAX_LED_R_NET1); + crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out); + } else + crisv32_led_net1_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + DEBUG(printk(KERN_DEBUG "crisv32_io_get: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + + DEBUG(printk(KERN_DEBUG + "crisv32_io_get_name: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif diff --git a/arch/cris/arch-v32/mach-fs/pinmux.c b/arch/cris/arch-v32/mach-fs/pinmux.c new file mode 100644 index 00000000000..d722ad9ae62 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/pinmux.c @@ -0,0 +1,309 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> + +#undef DEBUG + +#define PORT_PINS 18 +#define PORTS 4 + +static char pins[PORTS][PORT_PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = + pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; + REG_WR(pinmux, regi_pinmux, rw_pa, pa); + crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port][i] != pinmux_none) + && (pins[port][i] != pinmux_gpio) + && (pins[port][i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_sser0: + ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.sser0 = regk_pinmux_yes; + break; + case pinmux_sser1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + hwprot.sser1 = regk_pinmux_yes; + break; + case pinmux_ata0: + ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); + hwprot.ata0 = regk_pinmux_yes; + break; + case pinmux_ata1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); + hwprot.ata1 = regk_pinmux_yes; + break; + case pinmux_ata2: + ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata3: + ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata: + ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); + hwprot.ata = regk_pinmux_yes; + break; + case pinmux_eth1: + ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); + hwprot.eth1 = regk_pinmux_yes; + hwprot.eth1_mgm = regk_pinmux_yes; + break; + case pinmux_timer: + ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.timer = regk_pinmux_yes; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + + for (i = 0; i < PORT_PINS; i++) { + if (pins[port][i] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[port][i] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_C, 4, 7); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 11); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_C, 12, 15); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_sser0: + ret = crisv32_pinmux_dealloc(PORT_C, 0, 3); + ret |= crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.sser0 = regk_pinmux_no; + break; + case pinmux_sser1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + hwprot.sser1 = regk_pinmux_no; + break; + case pinmux_ata0: + ret = crisv32_pinmux_dealloc(PORT_D, 5, 7); + ret |= crisv32_pinmux_dealloc(PORT_D, 15, 17); + hwprot.ata0 = regk_pinmux_no; + break; + case pinmux_ata1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + ret |= crisv32_pinmux_dealloc(PORT_E, 17, 17); + hwprot.ata1 = regk_pinmux_no; + break; + case pinmux_ata2: + ret = crisv32_pinmux_dealloc(PORT_C, 11, 15); + ret |= crisv32_pinmux_dealloc(PORT_E, 3, 3); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata3: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 10); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 2); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata: + ret = crisv32_pinmux_dealloc(PORT_B, 0, 15); + ret |= crisv32_pinmux_dealloc(PORT_D, 8, 15); + hwprot.ata = regk_pinmux_no; + break; + case pinmux_eth1: + ret = crisv32_pinmux_dealloc(PORT_E, 0, 17); + hwprot.eth1 = regk_pinmux_no; + hwprot.eth1_mgm = regk_pinmux_no; + break; + case pinmux_timer: + ret = crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.timer = regk_pinmux_no; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_dump(void) +{ + int i, j; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + printk(KERN_DEBUG "Port %c\n", 'B' + i); + for (j = 0; j < PORT_PINS; j++) + printk(KERN_DEBUG " Pin %d = %d\n", j, pins[i][j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-fs/vcs_hook.c b/arch/cris/arch-v32/mach-fs/vcs_hook.c new file mode 100644 index 00000000000..593b10f07ef --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.c @@ -0,0 +1,100 @@ +/* + * Call simulator hook. This is the part running in the + * simulated program. + */ + +#include "vcs_hook.h" +#include <stdarg.h> +#include <asm/arch-v32/hwregs/reg_map.h> +#include <asm/arch-v32/hwregs/intr_vect_defs.h> + +#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ +#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ + +#define HOOK_DATA(offset) ((unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define VHOOK_DATA(offset) ((volatile unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)HOOK_MEM_BASE_ADDR)[offset] + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + unsigned i; + unsigned ret; +#ifdef USING_SOS + PREEMPT_OFF_SAVE(); +#endif + + /* pass parameters */ + HOOK_DATA(0) = id; + + /* Have to make hook_print_str a special case since we call with a + * parameter of byte type. Should perhaps be a separate + * hook_call. */ + + if (id == hook_print_str) { + int i; + char *str; + + HOOK_DATA(1) = pcnt; + + va_start(ap, pcnt); + str = (char *)va_arg(ap, unsigned); + + for (i = 0; i != pcnt; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + + HOOK_DATA_BYTE(8 + i) = 0; /* null byte */ + } else { + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + } + + /* read from mem to make sure data has propagated to memory before + * trigging */ + ret = *((volatile unsigned *)HOOK_MEM_BASE_ADDR); + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + +#ifdef USING_SOS + PREEMPT_RESTORE(); +#endif + return ret; +} + +unsigned hook_buf(unsigned i) +{ + return (HOOK_DATA(i)); +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_KICK_DOG(void) +{ + (void)hook_call(hook_kick_dog, 0); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ + (void)hook_call(hook_dog_timeout, 1, t); +} + diff --git a/arch/cris/arch-v32/kernel/vcs_hook.h b/arch/cris/arch-v32/mach-fs/vcs_hook.h index 7d73709e3cc..c000b9fece4 100644 --- a/arch/cris/arch-v32/kernel/vcs_hook.h +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.h @@ -1,11 +1,11 @@ -// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook functions +/* + * Call simulator hook functions + */ #ifndef HOOK_H #define HOOK_H -int hook_call( unsigned id, unsigned pcnt, ...); +int hook_call(unsigned id, unsigned pcnt, ...); enum hook_ids { hook_debug_on = 1, diff --git a/arch/cris/arch-v32/mm/Makefile b/arch/cris/arch-v32/mm/Makefile index 9146f88484b..0b801f2964a 100644 --- a/arch/cris/arch-v32/mm/Makefile +++ b/arch/cris/arch-v32/mm/Makefile @@ -1,3 +1,4 @@ # Makefile for the Linux/cris parts of the memory manager. -obj-y := mmu.o init.o tlb.o intmem.o +obj-y += mmu.o init.o tlb.o intmem.o +obj-$(CONFIG_ETRAX_L2CACHE) += l2cache.o diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c index a84ba7ff22d..5a9ac583464 100644 --- a/arch/cris/arch-v32/mm/init.c +++ b/arch/cris/arch-v32/mm/init.c @@ -65,7 +65,7 @@ cris_mmu_init(void) REG_STATE(mmu, rw_mm_cfg, seg_d, page) | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_STATE(mmu, rw_mm_cfg, seg_a, page) | #else REG_STATE(mmu, rw_mm_cfg, seg_a, linear) | @@ -84,13 +84,9 @@ cris_mmu_init(void) mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) | REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) | REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) | -#ifndef CONFIG_ETRAXFS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) | -#else - REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) | -#endif REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) | #else REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) | diff --git a/arch/cris/arch-v32/mm/intmem.c b/arch/cris/arch-v32/mm/intmem.c index 41ee7f7997f..9e8b69cdf19 100644 --- a/arch/cris/arch-v32/mm/intmem.c +++ b/arch/cris/arch-v32/mm/intmem.c @@ -7,11 +7,17 @@ #include <linux/list.h> #include <linux/slab.h> #include <asm/io.h> -#include <asm/arch/memmap.h> +#include <memmap.h> #define STATUS_FREE 0 #define STATUS_ALLOCATED 1 +#ifdef CONFIG_ETRAX_L2CACHE +#define RESERVED_SIZE 66*1024 +#else +#define RESERVED_SIZE 0 +#endif + struct intmem_allocation { struct list_head entry; unsigned int size; @@ -30,9 +36,10 @@ static void crisv32_intmem_init(void) struct intmem_allocation* alloc = (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL); INIT_LIST_HEAD(&intmem_allocations); - intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE); + intmem_virtual = ioremap(MEM_INTMEM_START + RESERVED_SIZE, + MEM_INTMEM_SIZE - RESERVED_SIZE); initiated = 1; - alloc->size = MEM_INTMEM_SIZE; + alloc->size = MEM_INTMEM_SIZE - RESERVED_SIZE; alloc->offset = 0; alloc->status = STATUS_FREE; list_add_tail(&alloc->entry, &intmem_allocations); @@ -59,19 +66,23 @@ void* crisv32_intmem_alloc(unsigned size, unsigned align) (struct intmem_allocation*) kmalloc(sizeof *alloc, GFP_ATOMIC); alloc->status = STATUS_FREE; - alloc->size = allocation->size - size - alignment; - alloc->offset = allocation->offset + size; + alloc->size = allocation->size - size - + alignment; + alloc->offset = allocation->offset + size + + alignment; list_add(&alloc->entry, &allocation->entry); if (alignment) { - struct intmem_allocation* tmp; - tmp = (struct intmem_allocation*) - kmalloc(sizeof *tmp, GFP_ATOMIC); + struct intmem_allocation *tmp; + tmp = (struct intmem_allocation *) + kmalloc(sizeof *tmp, + GFP_ATOMIC); tmp->offset = allocation->offset; tmp->size = alignment; tmp->status = STATUS_FREE; allocation->offset += alignment; - list_add_tail(&tmp->entry, &allocation->entry); + list_add_tail(&tmp->entry, + &allocation->entry); } } allocation->status = STATUS_ALLOCATED; @@ -96,22 +107,24 @@ void crisv32_intmem_free(void* addr) list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { if (allocation->offset == (int)(addr - intmem_virtual)) { - struct intmem_allocation* prev = + struct intmem_allocation *prev = list_entry(allocation->entry.prev, struct intmem_allocation, entry); - struct intmem_allocation* next = + struct intmem_allocation *next = list_entry(allocation->entry.next, struct intmem_allocation, entry); allocation->status = STATUS_FREE; /* Join with prev and/or next if also free */ - if (prev->status == STATUS_FREE) { + if ((prev != &intmem_allocations) && + (prev->status == STATUS_FREE)) { prev->size += allocation->size; list_del(&allocation->entry); kfree(allocation); allocation = prev; } - if (next->status == STATUS_FREE) { + if ((next != &intmem_allocations) && + (next->status == STATUS_FREE)) { allocation->size += next->size; list_del(&next->entry); kfree(next); @@ -125,15 +138,16 @@ void crisv32_intmem_free(void* addr) void* crisv32_intmem_phys_to_virt(unsigned long addr) { - return (void*)(addr - MEM_INTMEM_START+ - (unsigned long)intmem_virtual); + return (void *)(addr - (MEM_INTMEM_START + RESERVED_SIZE) + + (unsigned long)intmem_virtual); } unsigned long crisv32_intmem_virt_to_phys(void* addr) { return (unsigned long)((unsigned long )addr - - (unsigned long)intmem_virtual + MEM_INTMEM_START); + (unsigned long)intmem_virtual + MEM_INTMEM_START + + RESERVED_SIZE); } - +module_init(crisv32_intmem_init); diff --git a/arch/cris/arch-v32/mm/l2cache.c b/arch/cris/arch-v32/mm/l2cache.c new file mode 100644 index 00000000000..332ff10dcc6 --- /dev/null +++ b/arch/cris/arch-v32/mm/l2cache.c @@ -0,0 +1,29 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/l2cache_defs.h> +#include <asm/io.h> + +#define L2CACHE_SIZE 64 + +int __init l2cache_init(void) +{ + reg_l2cache_rw_ctrl ctrl = {0}; + reg_l2cache_rw_cfg cfg = {.en = regk_l2cache_yes}; + + ctrl.csize = L2CACHE_SIZE; + ctrl.cbase = L2CACHE_SIZE / 4 + (L2CACHE_SIZE % 4 ? 1 : 0); + REG_WR(l2cache, regi_l2cache, rw_ctrl, ctrl); + + /* Flush the tag memory */ + memset((void *)(MEM_INTMEM_START | MEM_NON_CACHEABLE), 0, 2*1024); + + /* Enable the cache */ + REG_WR(l2cache, regi_l2cache, rw_cfg, cfg); + + return 0; +} + diff --git a/arch/cris/arch-v32/mm/mmu.S b/arch/cris/arch-v32/mm/mmu.S index 27b70e5006a..2238d154bde 100644 --- a/arch/cris/arch-v32/mm/mmu.S +++ b/arch/cris/arch-v32/mm/mmu.S @@ -1,3 +1,5 @@ +; WARNING : The refill handler has been modified, see below !!! + /* * Copyright (C) 2003 Axis Communications AB * @@ -61,6 +63,14 @@ ; Note that the code is optimized to minimize stalls (makes the code harder ; to read). ; +; WARNING !!! +; Modified by Mikael Asker 060725: added a workaround for strange TLB +; behavior. If the same PTE is present in more than one set, the TLB +; doesn't recognize it and we get stuck in a loop of refill exceptions. +; The workaround detects such loops and exits them by flushing +; the TLB contents. The problem and workaround were verified +; in VCS by Mikael Starvik. +; ; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each ; PMD holds 16 MB of virtual memory. ; Bits 0-12 : Offset within a page @@ -68,6 +78,11 @@ ; Bits 24-31 : PMD offset within the PGD .macro MMU_REFILL_HANDLER handler, mmu + .data +1: .dword 0 ; refill_count + ; == 0 <=> last_refill_cause is invalid +2: .dword 0 ; last_refill_cause + .text .globl \handler \handler: subq 4, $sp @@ -76,42 +91,96 @@ subq 4, $sp move \mmu, $srs ; Select MMU support register bank move.d $acr, [$sp] - subq 4, $sp - move.d $r0, [$sp] + subq 12, $sp + move.d 1b, $acr ; Point to refill_count + movem $r2, [$sp] + + test.d [$acr] ; refill_count == 0 ? + beq 5f ; yes, last_refill_cause is invalid + move.d $acr, $r1 + + ; last_refill_cause is valid, investigate cause + addq 4, $r1 ; Point to last_refill_cause + move $s3, $r0 ; Get rw_mm_cause + move.d [$r1], $r2 ; Get last_refill_cause + cmp.d $r0, $r2 ; rw_mm_cause == last_refill_cause ? + beq 6f ; yes, increment count + moveq 1, $r2 + + ; rw_mm_cause != last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move.d $r0, [$r1] ; last_refill_cause = rw_mm_cause + +3: ; Probably not in a loop, continue normal processing #ifdef CONFIG_SMP move $s7, $acr ; PGD #else move.d per_cpu__current_pgd, $acr ; PGD #endif ; Look up PMD in PGD - move $s3, $r0 ; rw_mm_cause lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31) move.d [$acr], $acr ; PGD for the current process addi $r0.d, $acr, $acr move $s3, $r0 ; rw_mm_cause move.d [$acr], $acr ; Get PMD - beq 1f + beq 8f ; Look up PTE in PMD lsrq PAGE_SHIFT, $r0 and.w PAGE_MASK, $acr ; Remove PMD flags and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23) addi $r0.d, $acr, $acr move.d [$acr], $acr ; Get PTE - beq 2f - move.d [$sp+], $r0 ; Pop r0 in delayslot + beq 9f + movem [$sp], $r2 ; Restore r0-r2 in delay slot + addq 12, $sp ; Store in TLB move $acr, $s5 - ; Return +4: ; Return move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp rete rfe -1: ; PMD missing, let the mm subsystem fix it up. - move.d [$sp+], $r0 ; Pop r0 -2: ; PTE missing, let the mm subsystem fix it up. + +5: ; last_refill_cause is invalid + moveq 1, $r2 + addq 4, $r1 ; Point to last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move $s3, $r0 ; Get rw_mm_cause + ba 3b ; Continue normal processing + move.d $r0,[$r1] ; last_refill_cause = rw_mm_cause + +6: ; rw_mm_cause == last_refill_cause + move.d [$acr], $r2 ; Get refill_count + cmpq 4, $r2 ; refill_count > 4 ? + bhi 7f ; yes + addq 1, $r2 ; refill_count++ + ba 3b ; Continue normal processing + move.d $r2, [$acr] + +7: ; refill_count > 4, error + move.d $acr, $r0 ; Save pointer to refill_count + clear.d [$r0] ; refill_count = 0 + + ;; rewind the short stack + movem [$sp], $r2 ; Restore r0-r2 + addq 12, $sp + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + ;; Keep it simple (slow), save all the regs. + SAVE_ALL + jsr __flush_tlb_all + nop + ba ret_from_intr ; Return + nop + +8: ; PMD missing, let the mm subsystem fix it up. + movem [$sp], $r2 ; Restore r0-r2 +9: ; PTE missing, let the mm subsystem fix it up. + addq 12, $sp move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp SAVE_ALL move \mmu, $srs diff --git a/arch/cris/arch-v32/mm/tlb.c b/arch/cris/arch-v32/mm/tlb.c index a076ef6e938..eda5ebcaea5 100644 --- a/arch/cris/arch-v32/mm/tlb.c +++ b/arch/cris/arch-v32/mm/tlb.c @@ -13,8 +13,8 @@ #include <asm/arch/hwregs/supp_reg.h> #define UPDATE_TLB_SEL_IDX(val) \ -do { \ - unsigned long tlb_sel; \ +do { \ + unsigned long tlb_sel; \ \ tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, val); \ SUPP_REG_WR(RW_MM_TLB_SEL, tlb_sel); \ @@ -30,8 +30,8 @@ do { \ * The TLB can host up to 256 different mm contexts at the same time. The running * context is found in the PID register. Each TLB entry contains a page_id that * has to match the PID register to give a hit. page_id_map keeps track of which - * mm is assigned to which page_id, making sure it's known when to invalidate TLB - * entries. + * mm's is assigned to which page_id's, making sure it's known when to + * invalidate TLB entries. * * The last page_id is never running, it is used as an invalid page_id so that * it's possible to make TLB entries that will nerver match. @@ -179,29 +179,29 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - int cpu = smp_processor_id(); - - /* Make sure there is a MMU context. */ - spin_lock(&mmu_context_lock); - get_mmu_context(next); - cpu_set(cpu, next->cpu_vm_mask); - spin_unlock(&mmu_context_lock); - - /* - * Remember the pgd for the fault handlers. Keep a separate copy of it - * because current and active_mm might be invalid at points where - * there's still a need to derefer the pgd. - */ - per_cpu(current_pgd, cpu) = next->pgd; - - /* Switch context in the MMU. */ - if (tsk && task_thread_info(tsk)) - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls); - } - else - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); - } + if (prev != next) { + int cpu = smp_processor_id(); + + /* Make sure there is a MMU context. */ + spin_lock(&mmu_context_lock); + get_mmu_context(next); + cpu_set(cpu, next->cpu_vm_mask); + spin_unlock(&mmu_context_lock); + + /* + * Remember the pgd for the fault handlers. Keep a seperate + * copy of it because current and active_mm might be invalid + * at points where * there's still a need to derefer the pgd. + */ + per_cpu(current_pgd, cpu) = next->pgd; + + /* Switch context in the MMU. */ + if (tsk && task_thread_info(tsk)) { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | + task_thread_info(tsk)->tls); + } else { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); + } + } } diff --git a/arch/cris/arch-v32/vmlinux.lds.S b/arch/cris/arch-v32/vmlinux.lds.S index fead8c59ea6..d5f28e40717 100644 --- a/arch/cris/arch-v32/vmlinux.lds.S +++ b/arch/cris/arch-v32/vmlinux.lds.S @@ -9,6 +9,13 @@ */ #include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> + +#ifdef CONFIG_ETRAX_VMEM_SIZE +#define __CONFIG_ETRAX_VMEM_SIZE CONFIG_ETRAX_VMEM_SIZE +#else +#define __CONFIG_ETRAX_VMEM_SIZE 0 +#endif jiffies = jiffies_64; SECTIONS @@ -17,18 +24,19 @@ SECTIONS dram_start = .; ebp_start = .; - /* The boot section is only necessary until the VCS top level testbench */ - /* includes both flash and DRAM. */ + /* The boot section is only necessary until the VCS top */ + /* level testbench includes both flash and DRAM. */ .boot : { *(.boot) } - . = DRAM_VIRTUAL_BASE + 0x4000; /* See head.S and pages reserved at the start. */ + /* See head.S and pages reserved at the start. */ + . = DRAM_VIRTUAL_BASE + 0x4000; _text = .; /* Text and read-only data. */ text_start = .; /* Lots of aliases. */ _stext = .; __stext = .; .text : { - *(.text) + TEXT_TEXT SCHED_TEXT LOCK_TEXT *(.fixup) @@ -39,9 +47,9 @@ SECTIONS __etext = .; . = ALIGN(4); /* Exception table. */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; RODATA @@ -54,33 +62,27 @@ SECTIONS __edata = . ; /* End of data section. */ _edata = . ; - . = ALIGN(8192); /* init_task and stack, must be aligned. */ - .data.init_task : { *(.data.init_task) } + . = ALIGN(PAGE_SIZE); /* init_task and stack, must be aligned. */ + .data.init_task : { *(.data.init_task) } - . = ALIGN(8192); /* Init code and data. */ - __init_begin = .; + . = ALIGN(PAGE_SIZE); /* Init code and data. */ + __init_begin = .; .init.text : { _sinittext = .; INIT_TEXT _einittext = .; } .init.data : { INIT_DATA } - . = ALIGN(16); - __setup_start = .; - .init.setup : { *(.init.setup) } - __setup_end = .; + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; __start___param = .; __param : { *(__param) } __stop___param = .; - .initcall.init : { + .initcall.init : { __initcall_start = .; - *(.initcall1.init); - *(.initcall2.init); - *(.initcall3.init); - *(.initcall4.init); - *(.initcall5.init); - *(.initcall6.init); - *(.initcall7.init); + INITCALLS __initcall_end = .; } @@ -91,25 +93,23 @@ SECTIONS } SECURITY_INIT - PERCPU(8192) + __vmlinux_end = .; /* Last address of the physical file. */ + PERCPU(PAGE_SIZE) -#ifdef CONFIG_BLK_DEV_INITRD .init.ramfs : { __initramfs_start = .; *(.init.ramfs) __initramfs_end = .; - /* - * We fill to the next page, so we can discard all init - * pages without needing to consider what payload might be - * appended to the kernel image. - */ - FILL (0); - . = ALIGN (8192); } -#endif - __vmlinux_end = .; /* Last address of the physical file. */ - __init_end = .; + /* + * We fill to the next page, so we can discard all init + * pages without needing to consider what payload might be + * appended to the kernel image. + */ + . = ALIGN (PAGE_SIZE); + + __init_end = .; __data_end = . ; /* Move to _edata? */ __bss_start = .; /* BSS. */ @@ -123,11 +123,11 @@ SECTIONS __end = .; /* Sections to be discarded */ - /DISCARD/ : { + /DISCARD/ : { EXIT_TEXT EXIT_DATA *(.exitcall.exit) } - dram_end = dram_start + CONFIG_ETRAX_DRAM_SIZE*1024*1024; + dram_end = dram_start + (CONFIG_ETRAX_DRAM_SIZE - __CONFIG_ETRAX_VMEM_SIZE)*1024*1024; } |