summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v32/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/cris/arch-v32/kernel')
-rw-r--r--arch/cris/arch-v32/kernel/Makefile17
-rw-r--r--arch/cris/arch-v32/kernel/cache.c33
-rw-r--r--arch/cris/arch-v32/kernel/cacheflush.S99
-rw-r--r--arch/cris/arch-v32/kernel/crisksyms.c28
-rw-r--r--arch/cris/arch-v32/kernel/debugport.c233
-rw-r--r--arch/cris/arch-v32/kernel/entry.S889
-rw-r--r--arch/cris/arch-v32/kernel/fasttimer.c793
-rw-r--r--arch/cris/arch-v32/kernel/head.S440
-rw-r--r--arch/cris/arch-v32/kernel/irq.c520
-rw-r--r--arch/cris/arch-v32/kernel/kgdb.c1612
-rw-r--r--arch/cris/arch-v32/kernel/kgdb_asm.S551
-rw-r--r--arch/cris/arch-v32/kernel/process.c184
-rw-r--r--arch/cris/arch-v32/kernel/ptrace.c490
-rw-r--r--arch/cris/arch-v32/kernel/setup.c170
-rw-r--r--arch/cris/arch-v32/kernel/signal.c540
-rw-r--r--arch/cris/arch-v32/kernel/time.c342
-rw-r--r--arch/cris/arch-v32/kernel/traps.c193
17 files changed, 7134 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/Makefile b/arch/cris/arch-v32/kernel/Makefile
new file mode 100644
index 000000000..d9fc617ea
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux kernel.
+#
+
+extra-y := head.o
+
+
+obj-y := entry.o traps.o irq.o debugport.o \
+ process.o ptrace.o setup.o signal.o traps.o time.o \
+ cache.o cacheflush.o
+
+obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o
+obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
+obj-$(CONFIG_MODULES) += crisksyms.o
+
+clean:
+
diff --git a/arch/cris/arch-v32/kernel/cache.c b/arch/cris/arch-v32/kernel/cache.c
new file mode 100644
index 000000000..f38433b1f
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/cache.c
@@ -0,0 +1,33 @@
+#include <linux/module.h>
+#include <asm/io.h>
+#include <arch/cache.h>
+#include <arch/hwregs/dma.h>
+
+/* This file is used to workaround a cache bug, Guinness TR 106. */
+
+inline void flush_dma_descr(struct dma_descr_data *descr, int flush_buf)
+{
+ /* Flush descriptor to make sure we get correct in_eop and after. */
+ asm volatile ("ftagd [%0]" :: "r" (descr));
+ /* Flush buffer pointed out by descriptor. */
+ if (flush_buf)
+ cris_flush_cache_range(phys_to_virt((unsigned)descr->buf),
+ (unsigned)(descr->after - descr->buf));
+}
+EXPORT_SYMBOL(flush_dma_descr);
+
+void flush_dma_list(struct dma_descr_data *descr)
+{
+ while (1) {
+ flush_dma_descr(descr, 1);
+ if (descr->eol)
+ break;
+ descr = phys_to_virt((unsigned)descr->next);
+ }
+}
+EXPORT_SYMBOL(flush_dma_list);
+
+/* From cacheflush.S */
+EXPORT_SYMBOL(cris_flush_cache);
+/* From cacheflush.S */
+EXPORT_SYMBOL(cris_flush_cache_range);
diff --git a/arch/cris/arch-v32/kernel/cacheflush.S b/arch/cris/arch-v32/kernel/cacheflush.S
new file mode 100644
index 000000000..6fc3d95d7
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/cacheflush.S
@@ -0,0 +1,99 @@
+ .global cris_flush_cache_range
+ .type cris_flush_cache_range, @function
+cris_flush_cache_range:
+ move.d 1024, $r12
+ cmp.d $r11, $r12
+ bhi cris_flush_1KB
+ nop
+ add.d $r10, $r11
+ ftagd [$r10]
+cris_flush_last:
+ addq 32, $r10
+ cmp.d $r11, $r10
+ blt cris_flush_last
+ ftagd [$r10]
+ ret
+ nop
+cris_flush_1KB:
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ftagd [$r10]
+ addq 32, $r10
+ ba cris_flush_cache_range
+ sub.d $r12, $r11
+ .size cris_flush_cache_range, . - cris_flush_cache_range
+
+ .global cris_flush_cache
+ .type cris_flush_cache, @function
+cris_flush_cache:
+ moveq 0, $r10
+cris_flush_line:
+ move.d 16*1024, $r11
+ addq 16, $r10
+ cmp.d $r10, $r11
+ blt cris_flush_line
+ fidxd [$r10]
+ ret
+ nop
+ .size cris_flush_cache, . - cris_flush_cache
+
diff --git a/arch/cris/arch-v32/kernel/crisksyms.c b/arch/cris/arch-v32/kernel/crisksyms.c
new file mode 100644
index 000000000..bde8d1a10
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/crisksyms.c
@@ -0,0 +1,28 @@
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <arch/dma.h>
+#include <arch/intmem.h>
+#include <mach/pinmux.h>
+#include <arch/io.h>
+
+/* Functions for allocating DMA channels */
+EXPORT_SYMBOL(crisv32_request_dma);
+EXPORT_SYMBOL(crisv32_free_dma);
+
+/* Functions for handling internal RAM */
+EXPORT_SYMBOL(crisv32_intmem_alloc);
+EXPORT_SYMBOL(crisv32_intmem_free);
+EXPORT_SYMBOL(crisv32_intmem_phys_to_virt);
+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(crisv32_mask_irq);
+EXPORT_SYMBOL(crisv32_unmask_irq);
diff --git a/arch/cris/arch-v32/kernel/debugport.c b/arch/cris/arch-v32/kernel/debugport.c
new file mode 100644
index 000000000..02e33ebe5
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/debugport.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2003, Axis Communications AB.
+ */
+
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/ser_defs.h>
+#include <hwregs/dma_defs.h>
+#include <mach/pinmux.h>
+
+struct dbg_port
+{
+ unsigned char nbr;
+ unsigned long instance;
+ unsigned int started;
+ unsigned long baudrate;
+ unsigned char parity;
+ unsigned int bits;
+};
+
+struct dbg_port ports[] =
+{
+ {
+ 0,
+ regi_ser0,
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 1,
+ regi_ser1,
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 2,
+ regi_ser2,
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 3,
+ regi_ser3,
+ 0,
+ 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];
+#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
+ &ports[1];
+#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
+ &ports[2];
+#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
+ &ports[3];
+#elif defined(CONFIG_ETRAX_DEBUG_PORT4)
+ &ports[4];
+#else
+ NULL;
+#endif
+
+#ifdef CONFIG_ETRAX_KGDB
+static struct dbg_port *kgdb_port =
+#if defined(CONFIG_ETRAX_KGDB_PORT0)
+ &ports[0];
+#elif defined(CONFIG_ETRAX_KGDB_PORT1)
+ &ports[1];
+#elif defined(CONFIG_ETRAX_KGDB_PORT2)
+ &ports[2];
+#elif defined(CONFIG_ETRAX_KGDB_PORT3)
+ &ports[3];
+#elif defined(CONFIG_ETRAX_KGDB_PORT4)
+ &ports[4];
+#else
+ NULL;
+#endif
+#endif
+
+static void start_port(struct dbg_port *p)
+{
+ /* Set up serial port registers */
+ reg_ser_rw_tr_ctrl tr_ctrl = {0};
+ reg_ser_rw_tr_dma_en tr_dma_en = {0};
+
+ reg_ser_rw_rec_ctrl rec_ctrl = {0};
+ reg_ser_rw_tr_baud_div tr_baud_div = {0};
+ reg_ser_rw_rec_baud_div rec_baud_div = {0};
+
+ if (!p || p->started)
+ return;
+
+ p->started = 1;
+
+ if (p->nbr == 1)
+ crisv32_pinmux_alloc_fixed(pinmux_ser1);
+ else if (p->nbr == 2)
+ 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
+
+ tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493;
+ tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no;
+ tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8;
+ tr_ctrl.en = rec_ctrl.en = 1;
+
+ if (p->parity == 'O') {
+ tr_ctrl.par_en = regk_ser_yes;
+ tr_ctrl.par = regk_ser_odd;
+ rec_ctrl.par_en = regk_ser_yes;
+ rec_ctrl.par = regk_ser_odd;
+ } else if (p->parity == 'E') {
+ tr_ctrl.par_en = regk_ser_yes;
+ tr_ctrl.par = regk_ser_even;
+ rec_ctrl.par_en = regk_ser_yes;
+ rec_ctrl.par = regk_ser_odd;
+ }
+
+ if (p->bits == 7) {
+ tr_ctrl.data_bits = regk_ser_bits7;
+ rec_ctrl.data_bits = regk_ser_bits7;
+ }
+
+ REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div);
+ REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div);
+ REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en);
+ REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl);
+ REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl);
+}
+
+#ifdef CONFIG_ETRAX_KGDB
+/* Use polling to get a single character from the kernel debug port */
+int getDebugChar(void)
+{
+ reg_ser_rs_stat_din stat;
+ reg_ser_rw_ack_intr ack_intr = { 0 };
+
+ do {
+ stat = REG_RD(ser, kgdb_port->instance, rs_stat_din);
+ } while (!stat.dav);
+
+ /* Ack the data_avail interrupt. */
+ ack_intr.dav = 1;
+ REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr);
+
+ return stat.data;
+}
+
+/* Use polling to put a single character to the kernel debug port */
+void putDebugChar(int val)
+{
+ reg_ser_r_stat_din stat;
+ do {
+ 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 void __init early_putch(int c)
+{
+ reg_ser_r_stat_din stat;
+ /* 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, c);
+}
+
+static void __init
+early_console_write(struct console *con, const char *s, unsigned n)
+{
+ extern void reset_watchdog(void);
+ int i;
+
+ /* Send data. */
+ for (i = 0; i < n; i++) {
+ /* TODO: the '\n' -> '\n\r' translation should be done at the
+ receiver. Remove it when the serial driver removes it. */
+ if (s[i] == '\n')
+ early_putch('\r');
+ early_putch(s[i]);
+ reset_watchdog();
+ }
+}
+
+static struct console early_console_dev __initdata = {
+ .name = "early",
+ .write = early_console_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT,
+ .index = -1
+};
+
+/* Register console for printk's, etc. */
+int __init init_etrax_debug(void)
+{
+ start_port(port);
+
+ /* Register an early console if a debug port was chosen. */
+ register_console(&early_console_dev);
+
+#ifdef CONFIG_ETRAX_KGDB
+ start_port(kgdb_port);
+#endif /* CONFIG_ETRAX_KGDB */
+ return 0;
+}
diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S
new file mode 100644
index 000000000..026a0b21b
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/entry.S
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2000-2003 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ * Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
+ *
+ * Code for the system-call and fault low-level handling routines.
+ *
+ * NOTE: This code handles signal-recognition, which happens every time
+ * 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.
+ * 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
+ *
+ */
+
+#include <linux/linkage.h>
+#include <linux/sys.h>
+#include <asm/unistd.h>
+#include <asm/errno.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+
+#include <hwregs/asm/reg_map_asm.h>
+#include <hwregs/asm/intr_vect_defs_asm.h>
+
+ ;; Exported functions.
+ .globl system_call
+ .globl ret_from_intr
+ .globl ret_from_fork
+ .globl ret_from_kernel_thread
+ .globl resume
+ .globl multiple_interrupt
+ .globl nmi_interrupt
+ .globl spurious_interrupt
+ .globl do_sigtrap
+ .globl gdb_handle_exception
+ .globl sys_call_table
+
+ ; Check if preemptive kernel scheduling should be done.
+#ifdef CONFIG_PREEMPT
+_resume_kernel:
+ di
+ ; Load current task struct.
+ movs.w -8192, $r0 ; THREAD_SIZE = 8192
+ and.d $sp, $r0
+
+ addoq +TI_preempt_count, $r0, $acr
+ move.d [$acr], $r10 ; Preemption disabled?
+ bne _Rexit
+ nop
+
+_need_resched:
+ addoq +TI_flags, $r0, $acr
+ move.d [$acr], $r10
+ btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set.
+ bpl _Rexit
+ nop
+
+ ; Do preemptive kernel scheduling.
+ jsr preempt_schedule_irq
+ nop
+
+ ; Load new task struct.
+ movs.w -8192, $r0 ; THREAD_SIZE = 8192.
+ and.d $sp, $r0
+
+ ; One more time with new task.
+ ba _need_resched
+ nop
+#else
+#define _resume_kernel _Rexit
+#endif
+
+ ; Called at exit from fork. schedule_tail must be called to drop
+ ; spinlock if CONFIG_PREEMPT.
+ .type ret_from_fork,@function
+ret_from_fork:
+ jsr schedule_tail
+ nop
+ ba ret_from_sys_call
+ nop
+ .size ret_from_fork, . - ret_from_fork
+
+ .type ret_from_kernel_thread,@function
+ret_from_kernel_thread:
+ jsr schedule_tail
+ nop
+ move.d $r2, $r10
+ jsr $r1
+ nop
+ moveq 0, $r9 ; no syscall restarts, TYVM...
+ ba ret_from_sys_call
+ nop
+ .size ret_from_kernel_thread, . - ret_from_kernel_thread
+
+ .type ret_from_intr,@function
+ret_from_intr:
+ moveq 0, $r9 ; not a syscall
+
+ ;; Check for resched if preemptive kernel, or if we're going back to
+ ;; user-mode. This test matches the user_regs(regs) macro. Don't simply
+ ;; test CCS since that doesn't necessarily reflect what mode we'll
+ ;; return into.
+ addoq +PT_ccs, $sp, $acr
+ move.d [$acr], $r0
+ btstq 16, $r0 ; User-mode flag.
+ bpl _resume_kernel
+ .size ret_from_intr, . - ret_from_intr + 2 ; +2 includes the dslot.
+
+ ; Note that di below is in delay slot.
+ .type _resume_userspace,@function
+_resume_userspace:
+ di ; So need_resched and sigpending don't change.
+
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ addoq +TI_flags, $r0, $acr ; current->work
+ move.d [$acr], $r10
+ and.d _TIF_WORK_MASK, $r10 ; Work to be done on return?
+ bne _work_pending
+ nop
+ ba _Rexit
+ nop
+ .size _resume_userspace, . - _resume_userspace
+
+ ;; The system_call is called by a BREAK instruction, which looks pretty
+ ;; much like any other exception.
+ ;;
+ ;; System calls can't be made from interrupts but we still stack ERP
+ ;; to have a complete stack frame.
+ ;;
+ ;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12,
+ ;; r13,mof,srp
+ ;;
+ ;; This function looks on the _surface_ like spaghetti programming, but it's
+ ;; really designed so that the fast-path does not force cache-loading of
+ ;; non-used instructions. Only the non-common cases cause the outlined code
+ ;; to run..
+
+ .type system_call,@function
+system_call:
+ ;; Stack-frame similar to the irq heads, which is reversed in
+ ;; ret_from_sys_call.
+
+ sub.d 92, $sp ; Skip EDA.
+ movem $r13, [$sp]
+ move.d $sp, $r8
+ addq 14*4, $r8
+ move.d $acr, $r0
+ move $srs, $r1
+ move $mof, $r2
+ move $spc, $r3
+ move $ccs, $r4
+ move $srp, $r5
+ move $erp, $r6
+ move.d $r9, $r7 ; Store syscall number in EXS
+ subq 4, $sp
+ movem $r7, [$r8]
+ ei ; Enable interrupts while processing syscalls.
+ move.d $r10, [$sp]
+
+ ; Set S-bit when kernel debugging to keep hardware breakpoints active.
+#ifdef CONFIG_ETRAX_KGDB
+ move $ccs, $r0
+ or.d (1<<9), $r0
+ move $r0, $ccs
+#endif
+
+ movs.w -ENOSYS, $r0
+ addoq +PT_r10, $sp, $acr
+ move.d $r0, [$acr]
+
+ ;; Check if this process is syscall-traced.
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ addoq +TI_flags, $r0, $acr
+ move.d [$acr], $r0
+ btstq TIF_SYSCALL_TRACE, $r0
+ bmi _syscall_trace_entry
+ nop
+
+_syscall_traced:
+ ;; Check for sanity in the requested syscall number.
+ cmpu.w NR_syscalls, $r9
+ bhs ret_from_sys_call
+ lslq 2, $r9 ; Multiply by 4, in the delay slot.
+
+ ;; The location on the stack for the register structure is passed as a
+ ;; seventh argument. Some system calls need this.
+ move.d $sp, $r0
+ subq 4, $sp
+ move.d $r0, [$sp]
+
+ ;; The registers carrying parameters (R10-R13) are intact. The optional
+ ;; fifth and sixth parameters is in MOF and SRP respectively. Put them
+ ;; back on the stack.
+ subq 4, $sp
+ move $srp, [$sp]
+ subq 4, $sp
+ move $mof, [$sp]
+
+ ;; Actually to the system call.
+ addo.d +sys_call_table, $r9, $acr
+ move.d [$acr], $acr
+ jsr $acr
+ nop
+
+ addq 3*4, $sp ; Pop the mof, srp and regs parameters.
+ addoq +PT_r10, $sp, $acr
+ move.d $r10, [$acr] ; Save the return value.
+
+ moveq 1, $r9 ; "Parameter" to ret_from_sys_call to
+ ; show it was a sys call.
+
+ ;; Fall through into ret_from_sys_call to return.
+
+ret_from_sys_call:
+ ;; R9 is a parameter:
+ ;; >= 1 from syscall
+ ;; 0 from irq
+
+ ;; Get the current task-struct pointer.
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ di ; Make sure need_resched and sigpending don't change.
+
+ addoq +TI_flags, $r0, $acr
+ move.d [$acr], $r1
+ and.d _TIF_ALLWORK_MASK, $r1
+ bne _syscall_exit_work
+ nop
+ .size system_call, . - system_call
+
+ .type _Rexit,@function
+_Rexit:
+ ;; This epilogue MUST match the prologues in multiple_interrupt, irq.h
+ ;; and ptregs.h.
+ addq 4, $sp ; Skip orig_r10.
+ movem [$sp+], $r13 ; Registers R0-R13.
+ move.d [$sp+], $acr
+ move [$sp], $srs
+ addq 4, $sp
+ move [$sp+], $mof
+ move [$sp+], $spc
+ move [$sp+], $ccs
+ move [$sp+], $srp
+ move [$sp+], $erp
+ addq 8, $sp ; Skip EXS, EDA.
+ jump $erp
+ rfe ; Restore condition code stack in delay-slot.
+ .size _Rexit, . - _Rexit
+
+ ;; We get here after doing a syscall if extra work might need to be done
+ ;; perform syscall exit tracing if needed.
+
+ .type _syscall_exit_work,@function
+_syscall_exit_work:
+ ;; R0 contains current at this point and irq's are disabled.
+
+ addoq +TI_flags, $r0, $acr
+ move.d [$acr], $r1
+ btstq TIF_SYSCALL_TRACE, $r1
+ bpl _work_pending
+ nop
+ ei
+ move.d $r9, $r1 ; Preserve R9.
+ jsr do_syscall_trace
+ nop
+ move.d $r1, $r9
+ ba _resume_userspace
+ nop
+ .size _syscall_exit_work, . - _syscall_exit_work
+
+ .type _work_pending,@function
+_work_pending:
+ addoq +TI_flags, $r0, $acr
+ move.d [$acr], $r12 ; The thread_info_flags parameter.
+ move.d $sp, $r11 ; The regs param.
+ jsr do_work_pending
+ move.d $r9, $r10 ; The syscall/irq param.
+
+ ba _Rexit
+ nop
+ .size _work_pending, . - _work_pending
+
+ ;; We get here as a sidetrack when we've entered a syscall with the
+ ;; trace-bit set. We need to call do_syscall_trace and then continue
+ ;; with the call.
+
+_syscall_trace_entry:
+ ;; PT_r10 in the frame contains -ENOSYS as required, at this point.
+
+ jsr do_syscall_trace
+ nop
+
+ ;; Now re-enter the syscall code to do the syscall itself. We need to
+ ;; restore R9 here to contain the wanted syscall, and the other
+ ;; parameter-bearing registers.
+ addoq +PT_r9, $sp, $acr
+ move.d [$acr], $r9
+ addoq +PT_orig_r10, $sp, $acr
+ move.d [$acr], $r10 ; PT_r10 is already -ENOSYS.
+ addoq +PT_r11, $sp, $acr
+ move.d [$acr], $r11
+ addoq +PT_r12, $sp, $acr
+ move.d [$acr], $r12
+ addoq +PT_r13, $sp, $acr
+ move.d [$acr], $r13
+ addoq +PT_mof, $sp, $acr
+ move [$acr], $mof
+ addoq +PT_srp, $sp, $acr
+ move [$acr], $srp
+
+ ba _syscall_traced
+ nop
+
+ ;; Resume performs the actual task-switching, by switching stack
+ ;; pointers. Input arguments are:
+ ;;
+ ;; R10 = prev
+ ;; R11 = next
+ ;; R12 = thread offset in task struct.
+ ;;
+ ;; Returns old current in R10.
+
+ .type resume,@function
+resume:
+ subq 4, $sp ; Make space for srp.
+
+ add.d $r12, $r10 ; R10 = current tasks tss.
+ addoq +THREAD_ccs, $r10, $acr
+ move $srp, [$sp] ; Keep old/new PC on the stack.
+ move $ccs, [$acr] ; Save IRQ enable state.
+ di
+
+ addoq +THREAD_usp, $r10, $acr
+ subq 10*4, $sp ; Make room for R9.
+ move $usp, [$acr] ; Save user-mode stackpointer.
+
+ ;; See copy_thread for the reason why register R9 is saved.
+ movem $r9, [$sp] ; Save non-scratch registers and R9.
+
+ addoq +THREAD_ksp, $r10, $acr
+ move.d $sp, $r10 ; Return last running task in R10.
+ move.d $sp, [$acr] ; Save kernel SP for old task.
+
+ and.d -8192, $r10 ; Get thread_info from stackpointer.
+ addoq +TI_task, $r10, $acr
+ add.d $r12, $r11 ; Find the new tasks tss.
+ move.d [$acr], $r10 ; Get task.
+ addoq +THREAD_ksp, $r11, $acr
+ move.d [$acr], $sp ; Switch to new stackframe.
+ addoq +THREAD_usp, $r11, $acr
+ movem [$sp+], $r9 ; Restore non-scratch registers and R9.
+
+ move [$acr], $usp ; Restore user-mode stackpointer.
+
+ addoq +THREAD_ccs, $r11, $acr
+ move.d [$sp+], $r11
+ jump $r11 ; Restore PC.
+ move [$acr], $ccs ; Restore IRQ enable status.
+ .size resume, . - resume
+
+nmi_interrupt:
+
+;; If we receive a watchdog interrupt while it is not expected, then set
+;; up a canonical frame and dump register contents before dying.
+
+ ;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
+ subq 12, $sp ; Skip EXS, EDA.
+ move $nrp, [$sp]
+ subq 4, $sp
+ move $srp, [$sp]
+ subq 4, $sp
+ move $ccs, [$sp]
+ subq 4, $sp
+ move $spc, [$sp]
+ subq 4, $sp
+ move $mof, [$sp]
+ subq 4, $sp
+ move $srs, [$sp]
+ subq 4, $sp
+ move.d $acr, [$sp]
+ subq 14*4, $sp ; Make room for R0-R13.
+ movem $r13, [$sp] ; Push R0-R13.
+ subq 4, $sp
+ move.d $r10, [$sp] ; Push orig_r10.
+ move.d REG_ADDR(intr_vect, regi_irq, r_nmi), $r0
+ move.d [$r0], $r0
+ btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0
+ bpl 1f
+ nop
+ 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
+ nop
+ jsr handle_nmi
+ move.d $sp, $r10 ; Pointer to registers
+1: addq 4, $sp ; Skip orig_r10
+ movem [$sp+], $r13
+ move.d [$sp+], $acr
+ move [$sp], $srs
+ addq 4, $sp
+ move [$sp+], $mof
+ move [$sp+], $spc
+ move [$sp+], $ccs
+ move [$sp+], $srp
+ move [$sp+], $nrp
+ addq 8, $sp ; Skip EXS, EDA.
+ jump $nrp
+ rfn
+
+ .comm cause_of_death, 4 ;; Don't declare this anywhere.
+
+spurious_interrupt:
+ di
+ jump hard_reset_now
+ nop
+
+ ;; This handles the case when multiple interrupts arrive at the same
+ ;; time. Jump to the first set interrupt bit in a priority fashion. The
+ ;; hardware will call the unserved interrupts after the handler
+ ;; finishes.
+ .type multiple_interrupt, @function
+multiple_interrupt:
+ ;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
+ subq 12, $sp ; Skip EXS, EDA.
+ move $erp, [$sp]
+ subq 4, $sp
+ move $srp, [$sp]
+ subq 4, $sp
+ move $ccs, [$sp]
+ subq 4, $sp
+ move $spc, [$sp]
+ subq 4, $sp
+ move $mof, [$sp]
+ subq 4, $sp
+ move $srs, [$sp]
+ subq 4, $sp
+ move.d $acr, [$sp]
+ subq 14*4, $sp ; Make room for R0-R13.
+ movem $r13, [$sp] ; Push R0-R13.
+ subq 4, $sp
+ move.d $r10, [$sp] ; Push orig_r10.
+
+; Set S-bit when kernel debugging to keep hardware breakpoints active.
+#ifdef CONFIG_ETRAX_KGDB
+ move $ccs, $r0
+ or.d (1<<9), $r0
+ move $r0, $ccs
+#endif
+
+ jsr crisv32_do_multiple
+ move.d $sp, $r10
+ jump ret_from_intr
+ nop
+ .size multiple_interrupt, . - multiple_interrupt
+
+do_sigtrap:
+ ;; Sigtraps the process that executed the BREAK instruction. Creates a
+ ;; frame that Rexit expects.
+ subq 4, $sp
+ move $eda, [$sp]
+ subq 4, $sp
+ move $exs, [$sp]
+ subq 4, $sp
+ move $erp, [$sp]
+ subq 4, $sp
+ move $srp, [$sp]
+ subq 4, $sp
+ move $ccs, [$sp]
+ subq 4, $sp
+ move $spc, [$sp]
+ subq 4, $sp
+ move $mof, [$sp]
+ subq 4, $sp
+ move $srs, [$sp]
+ subq 4, $sp
+ move.d $acr, [$sp]
+ di ; Need to disable irq's at this point.
+ subq 14*4, $sp ; Make room for r0-r13.
+ movem $r13, [$sp] ; Push the r0-r13 registers.
+ subq 4, $sp
+ move.d $r10, [$sp] ; Push orig_r10.
+
+ movs.w -8192, $r9 ; THREAD_SIZE == 8192
+ and.d $sp, $r9
+
+ ;; thread_info as first parameter
+ move.d $r9, $r10
+ moveq 5, $r11 ; SIGTRAP as second argument.
+ jsr ugdb_trap_user
+ nop
+ jump ret_from_intr ; Use the return routine for interrupts.
+ nop
+
+gdb_handle_exception:
+ subq 4, $sp
+ move.d $r0, [$sp]
+#ifdef CONFIG_ETRAX_KGDB
+ move $ccs, $r0 ; U-flag not affected by previous insns.
+ btstq 16, $r0 ; Test the U-flag.
+ bmi _ugdb_handle_exception ; Go to user mode debugging.
+ nop ; Empty delay-slot (cannot pop R0 here).
+ ba kgdb_handle_exception ; Go to kernel debugging.
+ move.d [$sp+], $r0 ; Restore R0 in delay slot.
+#endif
+
+_ugdb_handle_exception:
+ ba do_sigtrap ; SIGTRAP the offending process.
+ move.d [$sp+], $r0 ; Restore R0 in delay slot.
+
+ .data
+
+ .section .rodata,"a"
+sys_call_table:
+ .long sys_restart_syscall ; 0 - old "setup()" system call, used
+ ; for restarting.
+ .long sys_exit
+ .long sys_fork
+ .long sys_read
+ .long sys_write
+ .long sys_open /* 5 */
+ .long sys_close
+ .long sys_waitpid
+ .long sys_creat
+ .long sys_link
+ .long sys_unlink /* 10 */
+ .long sys_execve
+ .long sys_chdir
+ .long sys_time
+ .long sys_mknod
+ .long sys_chmod /* 15 */
+ .long sys_lchown16
+ .long sys_ni_syscall /* old break syscall holder */
+ .long sys_stat
+ .long sys_lseek
+ .long sys_getpid /* 20 */
+ .long sys_mount
+ .long sys_oldumount
+ .long sys_setuid16
+ .long sys_getuid16
+ .long sys_stime /* 25 */
+ .long sys_ptrace
+ .long sys_alarm
+ .long sys_fstat
+ .long sys_pause
+ .long sys_utime /* 30 */
+ .long sys_ni_syscall /* old stty syscall holder */
+ .long sys_ni_syscall /* old gtty syscall holder */
+ .long sys_access
+ .long sys_nice
+ .long sys_ni_syscall /* 35 old ftime syscall holder */
+ .long sys_sync
+ .long sys_kill
+ .long sys_rename
+ .long sys_mkdir
+ .long sys_rmdir /* 40 */
+ .long sys_dup
+ .long sys_pipe
+ .long sys_times
+ .long sys_ni_syscall /* old prof syscall holder */
+ .long sys_brk /* 45 */
+ .long sys_setgid16
+ .long sys_getgid16
+ .long sys_signal
+ .long sys_geteuid16
+ .long sys_getegid16 /* 50 */
+ .long sys_acct
+ .long sys_umount /* recycled never used phys( */
+ .long sys_ni_syscall /* old lock syscall holder */
+ .long sys_ioctl
+ .long sys_fcntl /* 55 */
+ .long sys_ni_syscall /* old mpx syscall holder */
+ .long sys_setpgid
+ .long sys_ni_syscall /* old ulimit syscall holder */
+ .long sys_ni_syscall /* old sys_olduname holder */
+ .long sys_umask /* 60 */
+ .long sys_chroot
+ .long sys_ustat
+ .long sys_dup2
+ .long sys_getppid
+ .long sys_getpgrp /* 65 */
+ .long sys_setsid
+ .long sys_sigaction
+ .long sys_sgetmask
+ .long sys_ssetmask
+ .long sys_setreuid16 /* 70 */
+ .long sys_setregid16
+ .long sys_sigsuspend
+ .long sys_sigpending
+ .long sys_sethostname
+ .long sys_setrlimit /* 75 */
+ .long sys_old_getrlimit
+ .long sys_getrusage
+ .long sys_gettimeofday
+ .long sys_settimeofday
+ .long sys_getgroups16 /* 80 */
+ .long sys_setgroups16
+ .long sys_select /* was old_select in Linux/E100 */
+ .long sys_symlink
+ .long sys_lstat
+ .long sys_readlink /* 85 */
+ .long sys_uselib
+ .long sys_swapon
+ .long sys_reboot
+ .long sys_old_readdir
+ .long sys_old_mmap /* 90 */
+ .long sys_munmap
+ .long sys_truncate
+ .long sys_ftruncate
+ .long sys_fchmod
+ .long sys_fchown16 /* 95 */
+ .long sys_getpriority
+ .long sys_setpriority
+ .long sys_ni_syscall /* old profil syscall holder */
+ .long sys_statfs
+ .long sys_fstatfs /* 100 */
+ .long sys_ni_syscall /* sys_ioperm in i386 */
+ .long sys_socketcall
+ .long sys_syslog
+ .long sys_setitimer
+ .long sys_getitimer /* 105 */
+ .long sys_newstat
+ .long sys_newlstat
+ .long sys_newfstat
+ .long sys_ni_syscall /* old sys_uname holder */
+ .long sys_ni_syscall /* sys_iopl in i386 */
+ .long sys_vhangup
+ .long sys_ni_syscall /* old "idle" system call */
+ .long sys_ni_syscall /* vm86old in i386 */
+ .long sys_wait4
+ .long sys_swapoff /* 115 */
+ .long sys_sysinfo
+ .long sys_ipc
+ .long sys_fsync
+ .long sys_sigreturn
+ .long sys_clone /* 120 */
+ .long sys_setdomainname
+ .long sys_newuname
+ .long sys_ni_syscall /* sys_modify_ldt */
+ .long sys_adjtimex
+ .long sys_mprotect /* 125 */
+ .long sys_sigprocmask
+ .long sys_ni_syscall /* old "create_module" */
+ .long sys_init_module
+ .long sys_delete_module
+ .long sys_ni_syscall /* 130: old "get_kernel_syms" */
+ .long sys_quotactl
+ .long sys_getpgid
+ .long sys_fchdir
+ .long sys_bdflush
+ .long sys_sysfs /* 135 */
+ .long sys_personality
+ .long sys_ni_syscall /* for afs_syscall */
+ .long sys_setfsuid16
+ .long sys_setfsgid16
+ .long sys_llseek /* 140 */
+ .long sys_getdents
+ .long sys_select
+ .long sys_flock
+ .long sys_msync
+ .long sys_readv /* 145 */
+ .long sys_writev
+ .long sys_getsid
+ .long sys_fdatasync
+ .long sys_sysctl
+ .long sys_mlock /* 150 */
+ .long sys_munlock
+ .long sys_mlockall
+ .long sys_munlockall
+ .long sys_sched_setparam
+ .long sys_sched_getparam /* 155 */
+ .long sys_sched_setscheduler
+ .long sys_sched_getscheduler
+ .long sys_sched_yield
+ .long sys_sched_get_priority_max
+ .long sys_sched_get_priority_min /* 160 */
+ .long sys_sched_rr_get_interval
+ .long sys_nanosleep
+ .long sys_mremap
+ .long sys_setresuid16
+ .long sys_getresuid16 /* 165 */
+ .long sys_ni_syscall /* sys_vm86 */
+ .long sys_ni_syscall /* Old sys_query_module */
+ .long sys_poll
+ .long sys_ni_syscall /* Old nfsservctl */
+ .long sys_setresgid16 /* 170 */
+ .long sys_getresgid16
+ .long sys_prctl
+ .long sys_rt_sigreturn
+ .long sys_rt_sigaction
+ .long sys_rt_sigprocmask /* 175 */
+ .long sys_rt_sigpending
+ .long sys_rt_sigtimedwait
+ .long sys_rt_sigqueueinfo
+ .long sys_rt_sigsuspend
+ .long sys_pread64 /* 180 */
+ .long sys_pwrite64
+ .long sys_chown16
+ .long sys_getcwd
+ .long sys_capget
+ .long sys_capset /* 185 */
+ .long sys_sigaltstack
+ .long sys_sendfile
+ .long sys_ni_syscall /* streams1 */
+ .long sys_ni_syscall /* streams2 */
+ .long sys_vfork /* 190 */
+ .long sys_getrlimit
+ .long sys_mmap2
+ .long sys_truncate64
+ .long sys_ftruncate64
+ .long sys_stat64 /* 195 */
+ .long sys_lstat64
+ .long sys_fstat64
+ .long sys_lchown
+ .long sys_getuid
+ .long sys_getgid /* 200 */
+ .long sys_geteuid
+ .long sys_getegid
+ .long sys_setreuid
+ .long sys_setregid
+ .long sys_getgroups /* 205 */
+ .long sys_setgroups
+ .long sys_fchown
+ .long sys_setresuid
+ .long sys_getresuid
+ .long sys_setresgid /* 210 */
+ .long sys_getresgid
+ .long sys_chown
+ .long sys_setuid
+ .long sys_setgid
+ .long sys_setfsuid /* 215 */
+ .long sys_setfsgid
+ .long sys_pivot_root
+ .long sys_mincore
+ .long sys_madvise
+ .long sys_getdents64 /* 220 */
+ .long sys_fcntl64
+ .long sys_ni_syscall /* reserved for TUX */
+ .long sys_ni_syscall
+ .long sys_gettid
+ .long sys_readahead /* 225 */
+ .long sys_setxattr
+ .long sys_lsetxattr
+ .long sys_fsetxattr
+ .long sys_getxattr
+ .long sys_lgetxattr /* 230 */
+ .long sys_fgetxattr
+ .long sys_listxattr
+ .long sys_llistxattr
+ .long sys_flistxattr
+ .long sys_removexattr /* 235 */
+ .long sys_lremovexattr
+ .long sys_fremovexattr
+ .long sys_tkill
+ .long sys_sendfile64
+ .long sys_futex /* 240 */
+ .long sys_sched_setaffinity
+ .long sys_sched_getaffinity
+ .long sys_ni_syscall /* sys_set_thread_area */
+ .long sys_ni_syscall /* sys_get_thread_area */
+ .long sys_io_setup /* 245 */
+ .long sys_io_destroy
+ .long sys_io_getevents
+ .long sys_io_submit
+ .long sys_io_cancel
+ .long sys_fadvise64 /* 250 */
+ .long sys_ni_syscall
+ .long sys_exit_group
+ .long sys_lookup_dcookie
+ .long sys_epoll_create
+ .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_statfs64
+ .long sys_fstatfs64
+ .long sys_tgkill /* 270 */
+ .long sys_utimes
+ .long sys_fadvise64_64
+ .long sys_ni_syscall /* sys_vserver */
+ .long sys_ni_syscall /* sys_mbind */
+ .long sys_ni_syscall /* 275 sys_get_mempolicy */
+ .long sys_ni_syscall /* sys_set_mempolicy */
+ .long sys_mq_open
+ .long sys_mq_unlink
+ .long sys_mq_timedsend
+ .long sys_mq_timedreceive /* 280 */
+ .long sys_mq_notify
+ .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
+ .long sys_signalfd4
+ .long sys_eventfd2
+ .long sys_epoll_create1
+ .long sys_dup3 /* 330 */
+ .long sys_pipe2
+ .long sys_inotify_init1
+ .long sys_preadv
+ .long sys_pwritev
+ .long sys_setns /* 335 */
+ .long sys_name_to_handle_at
+ .long sys_open_by_handle_at
+ .long sys_rt_tgsigqueueinfo
+ .long sys_perf_event_open
+ .long sys_recvmmsg /* 340 */
+ .long sys_accept4
+ .long sys_fanotify_init
+ .long sys_fanotify_mark
+ .long sys_prlimit64
+ .long sys_clock_adjtime /* 345 */
+ .long sys_syncfs
+ .long sys_sendmmsg
+ .long sys_process_vm_readv
+ .long sys_process_vm_writev
+ .long sys_kcmp /* 350 */
+ .long sys_finit_module
+
+ /*
+ * NOTE!! This doesn't have to be exact - we just have
+ * to make sure we have _enough_ of the "sys_ni_syscall"
+ * entries. Don't panic if you notice that this hasn't
+ * been shrunk every time we add a new system call.
+ */
+
+ .rept NR_syscalls - (.-sys_call_table) / 4
+ .long sys_ni_syscall
+ .endr
+
diff --git a/arch/cris/arch-v32/kernel/fasttimer.c b/arch/cris/arch-v32/kernel/fasttimer.c
new file mode 100644
index 000000000..5c84dbb99
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/fasttimer.c
@@ -0,0 +1,793 @@
+/*
+ * linux/arch/cris/kernel/fasttimer.c
+ *
+ * Fast timers for ETRAX FS
+ *
+ * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <asm/irq.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>
+#include <linux/seq_file.h>
+
+/*
+ * timer0 is running at 100MHz and generating jiffies timer ticks
+ * at 100 or 1000 HZ.
+ * fasttimer gives an API that gives timers that expire "between" the jiffies
+ * giving microsecond resolution (10 ns).
+ * fasttimer uses reg_timer_rw_trig register to get interrupt when
+ * r_time reaches a certain value.
+ */
+
+
+#define DEBUG_LOG_INCLUDED
+#define FAST_TIMER_LOG
+/* #define FAST_TIMER_TEST */
+
+#define FAST_TIMER_SANITY_CHECKS
+
+#ifdef FAST_TIMER_SANITY_CHECKS
+static int sanity_failed;
+#endif
+
+#define D1(x)
+#define D2(x)
+#define DP(x)
+
+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;
+
+#ifdef DEBUG_LOG_INCLUDED
+#define DEBUG_LOG_MAX 128
+static const char * debug_log_string[DEBUG_LOG_MAX];
+static unsigned long debug_log_value[DEBUG_LOG_MAX];
+static unsigned int debug_log_cnt;
+static unsigned int debug_log_cnt_wrapped;
+
+#define DEBUG_LOG(string, value) \
+{ \
+ unsigned long log_flags; \
+ local_irq_save(log_flags); \
+ debug_log_string[debug_log_cnt] = (string); \
+ debug_log_value[debug_log_cnt] = (unsigned long)(value); \
+ if (++debug_log_cnt >= DEBUG_LOG_MAX) \
+ { \
+ debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
+ debug_log_cnt_wrapped = 1; \
+ } \
+ local_irq_restore(log_flags); \
+}
+#else
+#define DEBUG_LOG(string, value)
+#endif
+
+
+#define NUM_TIMER_STATS 16
+#ifdef FAST_TIMER_LOG
+struct fast_timer timer_added_log[NUM_TIMER_STATS];
+struct fast_timer timer_started_log[NUM_TIMER_STATS];
+struct fast_timer timer_expired_log[NUM_TIMER_STATS];
+#endif
+
+int timer_div_settings[NUM_TIMER_STATS];
+int timer_delay_settings[NUM_TIMER_STATS];
+
+struct work_struct fast_work;
+
+static void
+timer_trig_handler(struct work_struct *work);
+
+
+
+/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
+inline void do_gettimeofday_fast(struct fasttime_t *tv)
+{
+ tv->tv_jiff = jiffies;
+ tv->tv_usec = GET_JIFFIES_USEC();
+}
+
+inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
+{
+ /* 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 */
+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_time0;
+ reg_timer_r_time r_time1;
+ unsigned char trig_wrap;
+ unsigned char time_wrap;
+
+ 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_timer0, rw_intr_mask);
+ intr_mask.trig = 0;
+ REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
+
+ /* 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_timer0, rw_ack_intr, ack_intr);
+
+ /* Start timer */
+ REG_WR(timer, regi_timer0, rw_trig, trig);
+ trig_cfg.tmr = regk_timer_time;
+ REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
+
+ /* Check if we have already passed the trig time */
+ 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_timer0, rw_intr_mask);
+ intr_mask.trig = 1;
+ REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
+ fast_timers_started++;
+ fast_timer_running = 1;
+ } else {
+ /* We have passed the time, disable trig point, ack intr */
+ trig_cfg.tmr = regk_timer_off;
+ 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);
+ }
+
+}
+
+/* In version 1.4 this function takes 27 - 50 us */
+void start_one_shot_timer(struct fast_timer *t,
+ fast_timer_function_type *function,
+ unsigned long data,
+ unsigned long delay_us,
+ const char *name)
+{
+ unsigned long flags;
+ struct fast_timer *tmp;
+
+ D1(printk("sft %s %d us\n", name, delay_us));
+
+ local_irq_save(flags);
+
+ do_gettimeofday_fast(&t->tv_set);
+ 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;
+ t->data = data;
+ t->name = name;
+
+ t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
+ t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
+ if (t->tv_expires.tv_usec > 1000000) {
+ t->tv_expires.tv_usec -= 1000000;
+ t->tv_expires.tv_jiff += HZ;
+ }
+#ifdef FAST_TIMER_LOG
+ timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
+#endif
+ fast_timers_added++;
+
+ /* Check if this should timeout before anything else */
+ 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;
+#endif
+ start_timer_trig(delay_us);
+ } else {
+ /* Put in correct place in list */
+ while (tmp->next &&
+ fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0)
+ tmp = tmp->next;
+ /* Insert t after tmp */
+ t->prev = tmp;
+ t->next = tmp->next;
+ if (tmp->next)
+ {
+ tmp->next->prev = t;
+ }
+ tmp->next = t;
+ }
+
+ D2(printk("start_one_shot_timer: %d us done\n", delay_us));
+
+done:
+ local_irq_restore(flags);
+} /* start_one_shot_timer */
+
+static inline int fast_timer_pending (const struct fast_timer * t)
+{
+ return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
+}
+
+static inline int detach_fast_timer (struct fast_timer *t)
+{
+ struct fast_timer *next, *prev;
+ if (!fast_timer_pending(t))
+ return 0;
+ next = t->next;
+ prev = t->prev;
+ if (next)
+ next->prev = prev;
+ if (prev)
+ prev->next = next;
+ else
+ fast_timer_list = next;
+ fast_timers_deleted++;
+ return 1;
+}
+
+int del_fast_timer(struct fast_timer * t)
+{
+ unsigned long flags;
+ int ret;
+
+ local_irq_save(flags);
+ ret = detach_fast_timer(t);
+ t->next = t->prev = NULL;
+ local_irq_restore(flags);
+ return ret;
+} /* del_fast_timer */
+
+
+/* Interrupt routines or functions called in interrupt context */
+
+/* Timer interrupt handler for trig interrupts */
+
+static irqreturn_t
+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_timer0, r_masked_intr);
+ if (!masked_intr.trig)
+ return IRQ_NONE;
+ timer_trig_handler(NULL);
+ return IRQ_HANDLED;
+}
+
+static void timer_trig_handler(struct work_struct *work)
+{
+ reg_timer_rw_ack_intr ack_intr = { 0 };
+ reg_timer_rw_intr_mask intr_mask;
+ reg_timer_rw_trig_cfg trig_cfg = { 0 };
+ struct fast_timer *t;
+ unsigned long flags;
+
+ /* 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_timer0, rw_intr_mask);
+ intr_mask.trig = 0;
+ 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_timer0, rw_trig_cfg, trig_cfg);
+
+ /* Ack interrupt */
+ ack_intr.trig = 1;
+ REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
+
+ fast_timer_running = 0;
+ fast_timer_ints++;
+
+ fast_timer_function_type *f;
+ unsigned long d;
+
+ t = fast_timer_list;
+ while (t) {
+ struct fasttime_t tv;
+
+ /* Has it really expired? */
+ do_gettimeofday_fast(&tv);
+ D1(printk(KERN_DEBUG
+ "t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
+
+ 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;
+#endif
+ fast_timers_expired++;
+
+ /* Remove this timer before call, since it may reuse the timer */
+ 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;
+
+ /* 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 {
+ /* Timer is to early, let's set it again using the normal routines */
+ D1(printk(".\n"));
+ }
+
+ t = fast_timer_list;
+ if (t != NULL) {
+ /* Start next timer.. */
+ long us = 0;
+ struct fasttime_t tv;
+
+ do_gettimeofday_fast(&tv);
+
+ /* 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);
+ }
+ break;
+ } 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)
+ D1(printk("ttrig stop!\n"));
+}
+
+static void wake_up_func(unsigned long data)
+{
+ wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
+ wake_up(sleep_wait_p);
+}
+
+
+/* Useful API */
+
+void schedule_usleep(unsigned long us)
+{
+ struct fast_timer t;
+ wait_queue_head_t sleep_wait;
+ init_waitqueue_head(&sleep_wait);
+
+ D1(printk("schedule_usleep(%d)\n", us));
+ start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
+ "usleep");
+ /* 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 CONFIG_PROC_FS
+/* This value is very much based on testing */
+#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
+
+static int proc_fasttimer_show(struct seq_file *m, void *v)
+{
+ unsigned long flags;
+ int i = 0;
+ int num_to_show;
+ struct fasttime_t tv;
+ struct fast_timer *t, *nextt;
+
+ do_gettimeofday_fast(&tv);
+
+ seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
+ seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
+ seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
+ seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
+ seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
+ seq_printf(m, "Fast timer running: %s\n",
+ fast_timer_running ? "yes" : "no");
+ seq_printf(m, "Current time: %lu.%06lu\n",
+ (unsigned long)tv.tv_jiff,
+ (unsigned long)tv.tv_usec);
+#ifdef FAST_TIMER_SANITY_CHECKS
+ seq_printf(m, "Sanity failed: %i\n", sanity_failed);
+#endif
+ seq_putc(m, '\n');
+
+#ifdef DEBUG_LOG_INCLUDED
+ {
+ int end_i = debug_log_cnt;
+ i = 0;
+
+ if (debug_log_cnt_wrapped)
+ i = debug_log_cnt;
+
+ while ((i != end_i || debug_log_cnt_wrapped)) {
+ seq_printf(m, debug_log_string[i], debug_log_value[i]);
+ if (seq_has_overflowed(m))
+ return 0;
+ i = (i+1) % DEBUG_LOG_MAX;
+ }
+ }
+ seq_putc(m, '\n');
+#endif
+
+ num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers started: %i\n", fast_timers_started);
+ for (i = 0; i < num_to_show; i++) {
+ int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
+
+#if 1 //ndef FAST_TIMER_LOG
+ seq_printf(m, "div: %i delay: %i\n",
+ timer_div_settings[cur],
+ timer_delay_settings[cur]);
+#endif
+#ifdef FAST_TIMER_LOG
+ t = &timer_started_log[cur];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 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);
+ if (seq_has_overflowed(m))
+ return 0;
+#endif
+ }
+ seq_putc(m, '\n');
+
+#ifdef FAST_TIMER_LOG
+ num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers added: %i\n", fast_timers_added);
+ for (i = 0; i < num_to_show; i++) {
+ t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 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);
+ if (seq_has_overflowed(m))
+ return 0;
+ }
+ seq_putc(m, '\n');
+
+ num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers expired: %i\n", fast_timers_expired);
+ for (i = 0; i < num_to_show; i++){
+ t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 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);
+ if (seq_has_overflowed(m))
+ return 0;
+ }
+ seq_putc(m, '\n');
+#endif
+
+ seq_puts(m, "Active timers:\n");
+ local_irq_save(flags);
+ t = fast_timer_list;
+ while (t != NULL){
+ nextt = t->next;
+ local_irq_restore(flags);
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 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);
+ if (seq_has_overflowed(m))
+ return 0;
+ local_irq_save(flags);
+ if (t->next != nextt)
+ printk("timer removed!\n");
+ t = nextt;
+ }
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int proc_fasttimer_open(struct inode *inode, struct file *file)
+{
+ return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
+}
+
+static const struct file_operations proc_fasttimer_fops = {
+ .open = proc_fasttimer_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#endif /* PROC_FS */
+
+#ifdef FAST_TIMER_TEST
+static volatile unsigned long i = 0;
+static volatile int num_test_timeout = 0;
+static struct fast_timer tr[10];
+static int exp_num[10];
+
+static struct fasttime_t tv_exp[100];
+
+static void test_timeout(unsigned long data)
+{
+ do_gettimeofday_fast(&tv_exp[data]);
+ exp_num[data] = num_test_timeout;
+
+ num_test_timeout++;
+}
+
+static void test_timeout1(unsigned long data)
+{
+ do_gettimeofday_fast(&tv_exp[data]);
+ exp_num[data] = num_test_timeout;
+ if (data < 7)
+ {
+ start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1");
+ i++;
+ }
+ num_test_timeout++;
+}
+
+DP(
+static char buf0[2000];
+static char buf1[2000];
+static char buf2[2000];
+static char buf3[2000];
+static char buf4[2000];
+);
+
+static char buf5[6000];
+static int j_u[1000];
+
+static void fast_timer_test(void)
+{
+ int prev_num;
+ int j;
+
+ struct fasttime_t tv, tv0, tv1, tv2;
+
+ printk("fast_timer_test() start\n");
+ do_gettimeofday_fast(&tv);
+
+ for (j = 0; j < 1000; j++)
+ {
+ j_u[j] = GET_JIFFIES_USEC();
+ }
+ for (j = 0; j < 100; j++)
+ {
+ do_gettimeofday_fast(&tv_exp[j]);
+ }
+ printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
+
+ for (j = 0; j < 1000; j++)
+ {
+ 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(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);
+ start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0");
+ DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1");
+ DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2");
+ DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3");
+ DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx");
+ DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0));
+ i++;
+ do_gettimeofday_fast(&tv1);
+
+ proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+
+ prev_num = num_test_timeout;
+ while (num_test_timeout < i)
+ {
+ if (num_test_timeout != prev_num)
+ prev_num = num_test_timeout;
+ }
+ do_gettimeofday_fast(&tv2);
+ 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");
+ printk(buf1);
+ printk("buf2:\n");
+ printk(buf2);
+ printk("buf3:\n");
+ printk(buf3);
+ printk("buf4:\n");
+ printk(buf4);
+ );
+ printk("buf5:\n");
+ printk(buf5);
+
+ printk("timers set:\n");
+ for(j = 0; j<i; j++)
+ {
+ struct fast_timer *t = &tr[j];
+ printk("%-10s set: %6is %06ius exp: %6is %06ius "
+ "data: 0x%08X func: 0x%08X\n",
+ t->name,
+ t->tv_set.tv_jiff,
+ t->tv_set.tv_usec,
+ t->tv_expires.tv_jiff,
+ t->tv_expires.tv_usec,
+ t->data,
+ t->function
+ );
+
+ printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n",
+ t->delay_us,
+ tv_exp[j].tv_jiff,
+ tv_exp[j].tv_usec,
+ exp_num[j],
+ (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");
+ printk(buf5);
+ printk("fast_timer_test() done\n");
+}
+#endif
+
+
+int fast_timer_init(void)
+{
+ /* For some reason, request_irq() hangs when called froom time_init() */
+ if (!fast_timer_is_init)
+ {
+ printk("fast_timer_init()\n");
+
+#ifdef CONFIG_PROC_FS
+ proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
+#endif /* PROC_FS */
+ if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt,
+ IRQF_SHARED,
+ "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
new file mode 100644
index 000000000..74a66e0e3
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/head.S
@@ -0,0 +1,440 @@
+/*
+ * CRISv32 kernel startup code.
+ *
+ * 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 <arch/memmap.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/intr_vect.h>
+#include <hwregs/asm/mmu_defs_asm.h>
+#include <hwregs/asm/reg_map_asm.h>
+#include <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
+ ;; until they are read!
+
+ ;; Exported symbols.
+ .global etrax_irv
+ .global romfs_start
+ .global romfs_length
+ .global romfs_in_flash
+ .global nand_boot
+ .global swapper_pg_dir
+
+ .text
+tstart:
+ ;; This is the entry point of the kernel. The CPU is currently in
+ ;; supervisor mode.
+ ;;
+ ;; 0x00000000 if flash.
+ ;; 0x40004000 if DRAM.
+ ;;
+ di
+
+ START_CLOCKS
+
+ SETUP_WAIT_STATES
+
+ GIO_INIT
+
+ ;; Setup and enable the MMU. Use same configuration for both the data
+ ;; and the instruction MMU.
+ ;;
+ ;; Note; 3 cycles is needed for a bank-select to take effect. Further;
+ ;; bank 1 is the instruction MMU, bank 2 is the data MMU.
+
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+ 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_d, 5) \
+ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
+#else
+ 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
+#endif
+
+ ;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00.
+ move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \
+ | REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0), $r1
+
+ ;; Enable certain page protections and setup linear mapping
+ ;; for f,e,c,b,4,0.
+
+ ;; ARTPEC-3:
+ ;; c,d used for linear kernel mapping, up to 512 MB
+ ;; e used for vmalloc
+ ;; f unused, but page mapped to get page faults
+
+ ;; ETRAX FS:
+ ;; c used for linear kernel mapping, up to 256 MB
+ ;; d used for vmalloc
+ ;; e,f used for memory-mapped NOR flash
+
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+ 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) \
+ | REG_STATE(mmu, rw_mm_cfg, inv, on) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_f, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_e, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_d, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
+#else
+ 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) \
+ | REG_STATE(mmu, rw_mm_cfg, inv, on) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \
+ | 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) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
+ | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
+#endif
+
+ ;; Update instruction MMU.
+ move 1, $srs
+ nop
+ nop
+ nop
+ move $r0, $s2 ; kbase_hi.
+ move $r1, $s1 ; kbase_lo.
+ move $r2, $s0 ; mm_cfg, virtual memory configuration.
+
+ ;; Update data MMU.
+ move 2, $srs
+ nop
+ nop
+ nop
+ move $r0, $s2 ; kbase_hi.
+ move $r1, $s1 ; kbase_lo
+ move $r2, $s0 ; mm_cfg, virtual memory configuration.
+
+ ;; Enable data and instruction MMU.
+ move 0, $srs
+ moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on
+ nop
+ nop
+ nop
+ move $r0, $s0
+ nop
+ nop
+ nop
+
+ ; 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.
+ blo _inflash0
+ nop
+
+ jump _inram ; Jump to cached RAM.
+ nop
+
+ ;; Jumpgate.
+_inflash0:
+ jump _inflash
+ nop
+
+ ;; Put the following in a section so that storage for it can be
+ ;; reclaimed after init is finished.
+ .section ".init.text", "ax"
+
+_inflash:
+
+ ;; Initialize DRAM.
+ cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
+ beq _dram_initialized
+ nop
+
+#if defined CONFIG_ETRAXFS
+#include "../mach-fs/dram_init.S"
+#elif defined CONFIG_CRIS_MACH_ARTPEC3
+#include "../mach-a3/dram_init.S"
+#else
+#error Only ETRAXFS and ARTPEC-3 supported!
+#endif
+
+
+_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
+ move.d $r2, $r4
+ sub.d $r1, $r4
+1: move.w [$r0+], $r3
+ move.w $r3, [$r1+]
+ cmp.d $r2, $r1
+ blo 1b
+ nop
+
+ ;; Check for cramfs.
+ moveq 0, $r0
+ move.d romfs_length, $r1
+ move.d $r0, [$r1]
+ move.d [$r4], $r0 ; cramfs_super.magic
+ cmp.d CRAMFS_MAGIC, $r0
+ bne 1f
+ nop
+
+ ;; Set length and start of cramfs, set romfs_in_flash flag
+ addoq +4, $r4, $acr
+ move.d [$acr], $r0
+ move.d romfs_length, $r1
+ move.d $r0, [$r1]
+ add.d 0xf0000000, $r4 ; Add cached flash start in virtual memory.
+ move.d romfs_start, $r1
+ move.d $r4, [$r1]
+1: moveq 1, $r0
+ move.d romfs_in_flash, $r1
+ move.d $r0, [$r1]
+
+ jump _start_it ; Jump to cached code.
+ nop
+
+_inram:
+ ;; 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 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 ; continue with boot
+ nop
+
+move_cramfs:
+ ;; kernel is in DRAM.
+ ;; Must figure out if there is a piggybacked rootfs image or not.
+ ;; Set romfs_length to 0 => no rootfs image available by default.
+ moveq 0, $r0
+ move.d romfs_length, $r1
+ move.d $r0, [$r1]
+
+ ;; The kernel could have been unpacked to DRAM by the loader, but
+ ;; 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.
+ nop
+ ;; 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
+ move.d $r0, [$r1]
+ add.d 0xf0000000, $r9 ; Add cached flash start in virtual memory.
+ move.d romfs_start, $r1
+ move.d $r9, [$r1]
+ moveq 1, $r0
+ move.d romfs_in_flash, $r1
+ move.d $r0, [$r1]
+
+ jump _start_it ; Jump to cached code.
+ nop
+
+_no_romfs_in_flash:
+ ;; 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 __bss_start, $r0
+#else
+ move.d __end, $r0
+#endif
+ move.d [$r0], $r1
+ 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
+
+ move.d _end, $r1 ; start of cramfs -> r1
+ move.d romfs_start, $r3
+ move.d $r1, [$r3] ; store at romfs_start (for axisflashmap)
+ move.d romfs_length, $r3
+ move.d $r2, [$r3] ; store size at romfs_length
+
+ add.d $r2, $r0 ; copy from end and downwards
+ add.d $r2, $r1
+
+ lsrq 1, $r2 ; Size is in bytes, we copy words.
+ addq 1, $r2
+1:
+ move.w [$r0], $r3
+ move.w $r3, [$r1]
+ subq 2, $r0
+ subq 2, $r1
+ subq 1, $r2
+ bne 1b
+ nop
+
+4:
+ ;; BSS move done.
+ ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM
+ ;; Also clear nand_boot flag; if we got here, we know we've not
+ ;; booted from NAND flash.
+ moveq 0, $r0
+ move.d romfs_in_flash, $r1
+ move.d $r0, [$r1]
+ moveq 0, $r0
+ move.d nand_boot, $r1
+ move.d $r0, [$r1]
+
+ jump _start_it ; Jump to cached code.
+ nop
+
+_start_it:
+
+ ;; Check if kernel command line is supplied
+ cmp.d COMMAND_LINE_MAGIC, $r10
+ bne no_command_line
+ nop
+
+ move.d 256, $r13
+ move.d cris_command_line, $r10
+ or.d 0x80000000, $r11 ; Make it virtual
+1:
+ move.b [$r11+], $r1
+ move.b $r1, [$r10+]
+ subq 1, $r13
+ bne 1b
+ nop
+
+no_command_line:
+
+ ;; The kernel stack contains a task structure for each task. This
+ ;; the initial kernel stack is in the same page as the init_task,
+ ;; but starts at the top of the page, i.e. + 8192 bytes.
+ move.d init_thread_union + 8192, $sp
+ move.d ebp_start, $r0 ; Defined in linker-script.
+ move $r0, $ebp
+ move.d etrax_irv, $r1 ; Set the exception base register and pointer.
+ move.d $r0, [$r1]
+
+ ;; Clear the BSS region from _bss_start to _end.
+ move.d __bss_start, $r0
+ move.d _end, $r1
+1: clear.d [$r0+]
+ cmp.d $r1, $r0
+ blo 1b
+ nop
+
+ ; Initialize registers to increase determinism
+ 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
+
+; 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"
+
+#if defined CONFIG_ETRAXFS
+#include "../mach-fs/hw_settings.S"
+#elif defined CONFIG_CRIS_MACH_ARTPEC3
+#include "../mach-a3/hw_settings.S"
+#else
+#error Only ETRAXFS and ARTPEC-3 supported!
+#endif
diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c
new file mode 100644
index 000000000..6a881e0e9
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/irq.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2003, Axis Communications AB.
+ */
+
+#include <asm/irq.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/profile.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.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) */
+#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))
+#elif defined(CONFIG_ETRAX_KGDB_PORT1)
+#define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ))
+#elif defined(CONFIG_ETRAX_KGB_PORT2)
+#define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ))
+#elif defined(CONFIG_ETRAX_KGDB_PORT3)
+#define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ))
+#endif
+#endif
+
+DEFINE_SPINLOCK(irq_lock);
+
+struct cris_irq_allocation
+{
+ int cpu; /* The CPU to which the IRQ is currently allocated. */
+ cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */
+};
+
+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] =
+{
+ regi_irq,
+};
+
+#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];
+
+/* From irq.c. */
+extern void weird_irq(void);
+
+/* From entry.S. */
+extern void system_call(void);
+extern void nmi_interrupt(void);
+extern void multiple_interrupt(void);
+extern void gdb_handle_exception(void);
+extern void i_mmu_refill(void);
+extern void i_mmu_invalid(void);
+extern void i_mmu_access(void);
+extern void i_mmu_execute(void);
+extern void d_mmu_refill(void);
+extern void d_mmu_invalid(void);
+extern void d_mmu_access(void);
+extern void d_mmu_write(void);
+
+/* From kgdb.c. */
+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.
+ */
+#ifdef CONFIG_CRIS_MACH_ARTPEC3
+BUILD_TIMER_IRQ(0x31, 0)
+#else
+BUILD_IRQ(0x31)
+#endif
+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)
+#ifdef CONFIG_ETRAXFS
+BUILD_TIMER_IRQ(0x4b, 0)
+#else
+BUILD_IRQ(0x4b)
+#endif
+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[MACH_IRQS])(void) = {
+ IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt,
+ IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt,
+ IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt,
+ IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt,
+ IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt,
+ IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt,
+ IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt,
+ IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt,
+ IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt,
+ IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_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
+block_irq(int irq, int cpu)
+{
+ int intr_mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_lock, flags);
+ /* Remember, 1 let thru, 0 block. */
+ if (irq - FIRST_IRQ < 32) {
+ intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
+ rw_mask, 0);
+ intr_mask &= ~(1 << (irq - FIRST_IRQ));
+ REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
+ 0, intr_mask);
+ } else {
+ intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
+ rw_mask, 1);
+ intr_mask &= ~(1 << (irq - FIRST_IRQ - 32));
+ REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
+ 1, intr_mask);
+ }
+ spin_unlock_irqrestore(&irq_lock, flags);
+}
+
+void
+unblock_irq(int irq, int cpu)
+{
+ int intr_mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_lock, flags);
+ /* Remember, 1 let thru, 0 block. */
+ if (irq - FIRST_IRQ < 32) {
+ intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
+ rw_mask, 0);
+ intr_mask |= (1 << (irq - FIRST_IRQ));
+ REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
+ 0, intr_mask);
+ } else {
+ intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
+ rw_mask, 1);
+ intr_mask |= (1 << (irq - FIRST_IRQ - 32));
+ REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
+ 1, intr_mask);
+ }
+ spin_unlock_irqrestore(&irq_lock, flags);
+}
+
+/* Find out which CPU the irq should be allocated to. */
+static int irq_cpu(int irq)
+{
+ int cpu;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_lock, flags);
+ cpu = irq_allocations[irq - FIRST_IRQ].cpu;
+
+ /* Fixed interrupts stay on the local CPU. */
+ if (cpu == CPU_FIXED)
+ {
+ spin_unlock_irqrestore(&irq_lock, flags);
+ return smp_processor_id();
+ }
+
+
+ /* Let the interrupt stay if possible */
+ if (cpumask_test_cpu(cpu, &irq_allocations[irq - FIRST_IRQ].mask))
+ goto out;
+
+ /* IRQ must be moved to another CPU. */
+ cpu = cpumask_first(&irq_allocations[irq - FIRST_IRQ].mask);
+ irq_allocations[irq - FIRST_IRQ].cpu = cpu;
+out:
+ spin_unlock_irqrestore(&irq_lock, flags);
+ return cpu;
+}
+
+void crisv32_mask_irq(int irq)
+{
+ int cpu;
+
+ for (cpu = 0; cpu < NR_CPUS; cpu++)
+ block_irq(irq, cpu);
+}
+
+void crisv32_unmask_irq(int irq)
+{
+ unblock_irq(irq, irq_cpu(irq));
+}
+
+
+static void enable_crisv32_irq(struct irq_data *data)
+{
+ crisv32_unmask_irq(data->irq);
+}
+
+static void disable_crisv32_irq(struct irq_data *data)
+{
+ crisv32_mask_irq(data->irq);
+}
+
+static int set_affinity_crisv32_irq(struct irq_data *data,
+ const struct cpumask *dest, bool force)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_lock, flags);
+ irq_allocations[data->irq - FIRST_IRQ].mask = *dest;
+ spin_unlock_irqrestore(&irq_lock, flags);
+ return 0;
+}
+
+static struct irq_chip crisv32_irq_type = {
+ .name = "CRISv32",
+ .irq_shutdown = disable_crisv32_irq,
+ .irq_enable = enable_crisv32_irq,
+ .irq_disable = disable_crisv32_irq,
+ .irq_set_affinity = set_affinity_crisv32_irq,
+};
+
+void
+set_exception_vector(int n, irqvectptr addr)
+{
+ etrax_irv->v[n] = (irqvectptr) addr;
+}
+
+extern void do_IRQ(int irq, struct pt_regs * regs);
+
+void
+crisv32_do_IRQ(int irq, int block, struct pt_regs* regs)
+{
+ /* Interrupts that may not be moved to another CPU may
+ * skip blocking. This is currently only valid for the
+ * timer IRQ and the IPI and is used for the timer
+ * interrupt to avoid watchdog starvation.
+ */
+ if (!block) {
+ do_IRQ(irq, regs);
+ return;
+ }
+
+ block_irq(irq, smp_processor_id());
+ do_IRQ(irq, regs);
+
+ unblock_irq(irq, irq_cpu(irq));
+}
+
+/* If multiple interrupts occur simultaneously we get a multiple
+ * interrupt from the CPU and software has to sort out which
+ * interrupts that happened. There are two special cases here:
+ *
+ * 1. Timer interrupts may never be blocked because of the
+ * watchdog (refer to comment in include/asr/arch/irq.h)
+ * 2. GDB serial port IRQs are unhandled here and will be handled
+ * as a single IRQ when it strikes again because the GDB
+ * stubb wants to save the registers in its own fashion.
+ */
+void
+crisv32_do_multiple(struct pt_regs* regs)
+{
+ int cpu;
+ int mask;
+ int masked[NBR_REGS];
+ int bit;
+ int i;
+
+ cpu = smp_processor_id();
+
+ /* An extra irq_enter here to prevent softIRQs to run after
+ * each do_IRQ. This will decrease the interrupt latency.
+ */
+ irq_enter();
+
+ for (i = 0; i < NBR_REGS; i++) {
+ /* Get which IRQs that happened. */
+ 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_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
+ mask &= ~masked[i];
+
+ /* Timer IRQ is never masked */
+#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. */
+#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[0] &= ~IGNORE_MASK;
+#endif
+
+ /* Handle the rest of the IRQs. */
+ 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. */
+ 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();
+}
+
+static int crisv32_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &crisv32_irq_type, handle_simple_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops crisv32_irq_ops = {
+ .map = crisv32_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+/*
+ * This is called by start_kernel. It fixes the IRQ masks and setup the
+ * interrupt vector table to point to bad_interrupt pointers.
+ */
+void __init
+init_IRQ(void)
+{
+ int i;
+ int j;
+ reg_intr_vect_rw_mask vect_mask = {0};
+ struct device_node *np;
+ struct irq_domain *domain;
+
+ /* Clear all interrupts masks. */
+ 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;
+
+ np = of_find_compatible_node(NULL, NULL, "axis,crisv32-intc");
+ domain = irq_domain_add_legacy(np, NR_IRQS - FIRST_IRQ,
+ FIRST_IRQ, FIRST_IRQ,
+ &crisv32_irq_ops, NULL);
+ BUG_ON(!domain);
+ irq_set_default_host(domain);
+ of_node_put(np);
+
+ for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) {
+ set_exception_vector(i, interrupt[j]);
+ }
+
+ /* Mark Timer and IPI IRQs as CPU local */
+ irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
+ irq_set_status_flags(TIMER0_INTR_VECT, IRQ_PER_CPU);
+ irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
+ irq_set_status_flags(IPI_INTR_VECT, IRQ_PER_CPU);
+
+ set_exception_vector(0x00, nmi_interrupt);
+ set_exception_vector(0x30, multiple_interrupt);
+
+ /* Set up handler for various MMU bus faults. */
+ set_exception_vector(0x04, i_mmu_refill);
+ set_exception_vector(0x05, i_mmu_invalid);
+ set_exception_vector(0x06, i_mmu_access);
+ set_exception_vector(0x07, i_mmu_execute);
+ set_exception_vector(0x08, d_mmu_refill);
+ set_exception_vector(0x09, d_mmu_invalid);
+ 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);
+
+ /* Exception handlers for debugging, both user-mode and kernel-mode. */
+
+ /* Break 8. */
+ set_exception_vector(0x18, gdb_handle_exception);
+ /* Hardware single step. */
+ set_exception_vector(0x3, gdb_handle_exception);
+ /* Hardware breakpoint. */
+ set_exception_vector(0xc, gdb_handle_exception);
+
+#ifdef CONFIG_ETRAX_KGDB
+ kgdb_init();
+ /* Everything is set up; now trap the kernel. */
+ breakpoint();
+#endif
+}
+
diff --git a/arch/cris/arch-v32/kernel/kgdb.c b/arch/cris/arch-v32/kernel/kgdb.c
new file mode 100644
index 000000000..b06813aeb
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/kgdb.c
@@ -0,0 +1,1612 @@
+/*
+ * arch/cris/arch-v32/kernel/kgdb.c
+ *
+ * CRIS v32 version by Orjan Friberg, Axis Communications AB.
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *
+ * Originally written by Glenn Engel, Lake Stevens Instrument Division
+ *
+ * Contributed by HP Systems
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
+ * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
+ *
+ * Copyright (C) 1995 Andreas Busse
+ */
+
+/* FIXME: Check the documentation. */
+
+/*
+ * kgdb usage notes:
+ * -----------------
+ *
+ * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be
+ * built with different gcc flags: "-g" is added to get debug infos, and
+ * "-fomit-frame-pointer" is omitted to make debugging easier. Since the
+ * resulting kernel will be quite big (approx. > 7 MB), it will be stripped
+ * before compresion. Such a kernel will behave just as usually, except if
+ * given a "debug=<device>" command line option. (Only serial devices are
+ * allowed for <device>, i.e. no printers or the like; possible values are
+ * machine depedend and are the same as for the usual debug device, the one
+ * for logging kernel messages.) If that option is given and the device can be
+ * initialized, the kernel will connect to the remote gdb in trap_init(). The
+ * serial parameters are fixed to 8N1 and 115200 bps, for easyness of
+ * implementation.
+ *
+ * To start a debugging session, start that gdb with the debugging kernel
+ * image (the one with the symbols, vmlinux.debug) named on the command line.
+ * This file will be used by gdb to get symbol and debugging infos about the
+ * kernel. Next, select remote debug mode by
+ * target remote <device>
+ * where <device> is the name of the serial device over which the debugged
+ * machine is connected. Maybe you have to adjust the baud rate by
+ * set remotebaud <rate>
+ * or also other parameters with stty:
+ * shell stty ... </dev/...
+ * If the kernel to debug has already booted, it waited for gdb and now
+ * connects, and you'll see a breakpoint being reported. If the kernel isn't
+ * running yet, start it now. The order of gdb and the kernel doesn't matter.
+ * Another thing worth knowing about in the getting-started phase is how to
+ * debug the remote protocol itself. This is activated with
+ * set remotedebug 1
+ * gdb will then print out each packet sent or received. You'll also get some
+ * messages about the gdb stub on the console of the debugged machine.
+ *
+ * If all that works, you can use lots of the usual debugging techniques on
+ * the kernel, e.g. inspecting and changing variables/memory, setting
+ * breakpoints, single stepping and so on. It's also possible to interrupt the
+ * debugged kernel by pressing C-c in gdb. Have fun! :-)
+ *
+ * The gdb stub is entered (and thus the remote gdb gets control) in the
+ * following situations:
+ *
+ * - If breakpoint() is called. This is just after kgdb initialization, or if
+ * a breakpoint() call has been put somewhere into the kernel source.
+ * (Breakpoints can of course also be set the usual way in gdb.)
+ * In eLinux, we call breakpoint() in init/main.c after IRQ initialization.
+ *
+ * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel()
+ * are entered. All the CPU exceptions are mapped to (more or less..., see
+ * the hard_trap_info array below) appropriate signal, which are reported
+ * to gdb. die_if_kernel() is usually called after some kind of access
+ * error and thus is reported as SIGSEGV.
+ *
+ * - When panic() is called. This is reported as SIGABRT.
+ *
+ * - If C-c is received over the serial line, which is treated as
+ * SIGINT.
+ *
+ * Of course, all these signals are just faked for gdb, since there is no
+ * signal concept as such for the kernel. It also isn't possible --obviously--
+ * to set signal handlers from inside gdb, or restart the kernel with a
+ * signal.
+ *
+ * Current limitations:
+ *
+ * - While the kernel is stopped, interrupts are disabled for safety reasons
+ * (i.e., variables not changing magically or the like). But this also
+ * means that the clock isn't running anymore, and that interrupts from the
+ * hardware may get lost/not be served in time. This can cause some device
+ * errors...
+ *
+ * - When single-stepping, only one instruction of the current thread is
+ * executed, but interrupts are allowed for that time and will be serviced
+ * if pending. Be prepared for that.
+ *
+ * - All debugging happens in kernel virtual address space. There's no way to
+ * access physical memory not mapped in kernel space, or to access user
+ * space. A way to work around this is using get_user_long & Co. in gdb
+ * expressions, but only for the current process.
+ *
+ * - Interrupting the kernel only works if interrupts are currently allowed,
+ * and the interrupt of the serial line isn't blocked by some other means
+ * (IPL too high, disabled, ...)
+ *
+ * - The gdb stub is currently not reentrant, i.e. errors that happen therein
+ * (e.g. accessing invalid memory) may not be caught correctly. This could
+ * be removed in future by introducing a stack of struct registers.
+ *
+ */
+
+/*
+ * To enable debugger support, two things need to happen. One, a
+ * call to kgdb_init() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint().
+ *
+ * The following gdb commands are supported:
+ *
+ * command function Return value
+ *
+ * g return the value of the CPU registers hex data or ENN
+ * G set the value of the CPU registers OK or ENN
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
+ * baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum. A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ */
+
+
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/linkage.h>
+#include <linux/reboot.h>
+
+#include <asm/setup.h>
+#include <asm/ptrace.h>
+
+#include <asm/irq.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/intr_vect_defs.h>
+#include <hwregs/ser_defs.h>
+
+/* From entry.S. */
+extern void gdb_handle_exception(void);
+/* From kgdb_asm.S. */
+extern void kgdb_handle_exception(void);
+
+static int kgdb_started = 0;
+
+/********************************* Register image ****************************/
+
+typedef
+struct register_image
+{
+ /* Offset */
+ unsigned int r0; /* 0x00 */
+ unsigned int r1; /* 0x04 */
+ unsigned int r2; /* 0x08 */
+ unsigned int r3; /* 0x0C */
+ unsigned int r4; /* 0x10 */
+ unsigned int r5; /* 0x14 */
+ unsigned int r6; /* 0x18 */
+ unsigned int r7; /* 0x1C */
+ unsigned int r8; /* 0x20; Frame pointer (if any) */
+ unsigned int r9; /* 0x24 */
+ unsigned int r10; /* 0x28 */
+ unsigned int r11; /* 0x2C */
+ unsigned int r12; /* 0x30 */
+ unsigned int r13; /* 0x34 */
+ unsigned int sp; /* 0x38; R14, Stack pointer */
+ unsigned int acr; /* 0x3C; R15, Address calculation register. */
+
+ unsigned char bz; /* 0x40; P0, 8-bit zero register */
+ unsigned char vr; /* 0x41; P1, Version register (8-bit) */
+ unsigned int pid; /* 0x42; P2, Process ID */
+ unsigned char srs; /* 0x46; P3, Support register select (8-bit) */
+ unsigned short wz; /* 0x47; P4, 16-bit zero register */
+ unsigned int exs; /* 0x49; P5, Exception status */
+ unsigned int eda; /* 0x4D; P6, Exception data address */
+ unsigned int mof; /* 0x51; P7, Multiply overflow register */
+ unsigned int dz; /* 0x55; P8, 32-bit zero register */
+ unsigned int ebp; /* 0x59; P9, Exception base pointer */
+ unsigned int erp; /* 0x5D; P10, Exception return pointer. Contains the PC we are interested in. */
+ unsigned int srp; /* 0x61; P11, Subroutine return pointer */
+ unsigned int nrp; /* 0x65; P12, NMI return pointer */
+ unsigned int ccs; /* 0x69; P13, Condition code stack */
+ unsigned int usp; /* 0x6D; P14, User mode stack pointer */
+ unsigned int spc; /* 0x71; P15, Single step PC */
+ unsigned int pc; /* 0x75; Pseudo register (for the most part set to ERP). */
+
+} registers;
+
+typedef
+struct bp_register_image
+{
+ /* Support register bank 0. */
+ unsigned int s0_0;
+ unsigned int s1_0;
+ unsigned int s2_0;
+ unsigned int s3_0;
+ unsigned int s4_0;
+ unsigned int s5_0;
+ unsigned int s6_0;
+ unsigned int s7_0;
+ unsigned int s8_0;
+ unsigned int s9_0;
+ unsigned int s10_0;
+ unsigned int s11_0;
+ unsigned int s12_0;
+ unsigned int s13_0;
+ unsigned int s14_0;
+ unsigned int s15_0;
+
+ /* Support register bank 1. */
+ unsigned int s0_1;
+ unsigned int s1_1;
+ unsigned int s2_1;
+ unsigned int s3_1;
+ unsigned int s4_1;
+ unsigned int s5_1;
+ unsigned int s6_1;
+ unsigned int s7_1;
+ unsigned int s8_1;
+ unsigned int s9_1;
+ unsigned int s10_1;
+ unsigned int s11_1;
+ unsigned int s12_1;
+ unsigned int s13_1;
+ unsigned int s14_1;
+ unsigned int s15_1;
+
+ /* Support register bank 2. */
+ unsigned int s0_2;
+ unsigned int s1_2;
+ unsigned int s2_2;
+ unsigned int s3_2;
+ unsigned int s4_2;
+ unsigned int s5_2;
+ unsigned int s6_2;
+ unsigned int s7_2;
+ unsigned int s8_2;
+ unsigned int s9_2;
+ unsigned int s10_2;
+ unsigned int s11_2;
+ unsigned int s12_2;
+ unsigned int s13_2;
+ unsigned int s14_2;
+ unsigned int s15_2;
+
+ /* Support register bank 3. */
+ unsigned int s0_3; /* BP_CTRL */
+ unsigned int s1_3; /* BP_I0_START */
+ unsigned int s2_3; /* BP_I0_END */
+ unsigned int s3_3; /* BP_D0_START */
+ unsigned int s4_3; /* BP_D0_END */
+ unsigned int s5_3; /* BP_D1_START */
+ unsigned int s6_3; /* BP_D1_END */
+ unsigned int s7_3; /* BP_D2_START */
+ unsigned int s8_3; /* BP_D2_END */
+ unsigned int s9_3; /* BP_D3_START */
+ unsigned int s10_3; /* BP_D3_END */
+ unsigned int s11_3; /* BP_D4_START */
+ unsigned int s12_3; /* BP_D4_END */
+ unsigned int s13_3; /* BP_D5_START */
+ unsigned int s14_3; /* BP_D5_END */
+ unsigned int s15_3; /* BP_RESERVED */
+
+} support_registers;
+
+enum register_name
+{
+ R0, R1, R2, R3,
+ R4, R5, R6, R7,
+ R8, R9, R10, R11,
+ R12, R13, SP, ACR,
+
+ BZ, VR, PID, SRS,
+ WZ, EXS, EDA, MOF,
+ DZ, EBP, ERP, SRP,
+ NRP, CCS, USP, SPC,
+ PC,
+
+ S0, S1, S2, S3,
+ S4, S5, S6, S7,
+ S8, S9, S10, S11,
+ S12, S13, S14, S15
+
+};
+
+/* The register sizes of the registers in register_name. An unimplemented register
+ is designated by size 0 in this array. */
+static int register_size[] =
+{
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+
+ 1, 1, 4, 1,
+ 2, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+
+ 4,
+
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4
+
+};
+
+/* Contains the register image of the kernel.
+ (Global so that they can be reached from assembler code.) */
+registers reg;
+support_registers sreg;
+
+/************** Prototypes for local library functions ***********************/
+
+/* Copy of strcpy from libc. */
+static char *gdb_cris_strcpy(char *s1, const char *s2);
+
+/* Copy of strlen from libc. */
+static int gdb_cris_strlen(const char *s);
+
+/* Copy of memchr from libc. */
+static void *gdb_cris_memchr(const void *s, int c, int n);
+
+/* Copy of strtol from libc. Does only support base 16. */
+static int gdb_cris_strtol(const char *s, char **endptr, int base);
+
+/********************** Prototypes for local functions. **********************/
+
+/* Write a value to a specified register regno in the register image
+ of the current thread. */
+static int write_register(int regno, char *val);
+
+/* Read a value from a specified register in the register image. Returns the
+ status of the read operation. The register value is returned in valptr. */
+static int read_register(char regno, unsigned int *valptr);
+
+/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */
+int getDebugChar(void);
+
+/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */
+void putDebugChar(int val);
+
+/* Returns the integer equivalent of a hexadecimal character. */
+static int hex(char ch);
+
+/* Convert the memory, pointed to by mem into hexadecimal representation.
+ Put the result in buf, and return a pointer to the last character
+ in buf (null). */
+static char *mem2hex(char *buf, unsigned char *mem, int count);
+
+/* Convert the array, in hexadecimal representation, pointed to by buf into
+ binary representation. Put the result in mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char *hex2mem(unsigned char *mem, char *buf, int count);
+
+/* Put the content of the array, in binary representation, pointed to by buf
+ into memory pointed to by mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char *bin2mem(unsigned char *mem, unsigned char *buf, int count);
+
+/* Await the sequence $<data>#<checksum> and store <data> in the array buffer
+ returned. */
+static void getpacket(char *buffer);
+
+/* Send $<data>#<checksum> from the <data> in the array buffer. */
+static void putpacket(char *buffer);
+
+/* Build and send a response packet in order to inform the host the
+ stub is stopped. */
+static void stub_is_stopped(int sigval);
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. Not static since it needs to be reached
+ from assembler code. */
+void handle_exception(int sigval);
+
+/* Performs a complete re-start from scratch. ETRAX specific. */
+static void kill_restart(void);
+
+/******************** Prototypes for global functions. ***********************/
+
+/* The string str is prepended with the GDB printout token and sent. */
+void putDebugString(const unsigned char *str, int len);
+
+/* A static breakpoint to be used at startup. */
+void breakpoint(void);
+
+/* Avoid warning as the internal_stack is not used in the C-code. */
+#define USEDVAR(name) { if (name) { ; } }
+#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) }
+
+/********************************** Packet I/O ******************************/
+/* BUFMAX defines the maximum number of characters in
+ inbound/outbound buffers */
+/* FIXME: How do we know it's enough? */
+#define BUFMAX 512
+
+/* Run-length encoding maximum length. Send 64 at most. */
+#define RUNLENMAX 64
+
+/* The inbound/outbound buffers used in packet I/O */
+static char input_buffer[BUFMAX];
+static char output_buffer[BUFMAX];
+
+/* Error and warning messages. */
+enum error_type
+{
+ SUCCESS, E01, E02, E03, E04, E05, E06,
+};
+
+static char *error_message[] =
+{
+ "",
+ "E01 Set current or general thread - H[c,g] - internal error.",
+ "E02 Change register content - P - cannot change read-only register.",
+ "E03 Thread is not alive.", /* T, not used. */
+ "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.",
+ "E05 Change register content - P - the register is not implemented..",
+ "E06 Change memory content - M - internal error.",
+};
+
+/********************************** Breakpoint *******************************/
+/* Use an internal stack in the breakpoint and interrupt response routines.
+ FIXME: How do we know the size of this stack is enough?
+ Global so it can be reached from assembler code. */
+#define INTERNAL_STACK_SIZE 1024
+char internal_stack[INTERNAL_STACK_SIZE];
+
+/* Due to the breakpoint return pointer, a state variable is needed to keep
+ track of whether it is a static (compiled) or dynamic (gdb-invoked)
+ breakpoint to be handled. A static breakpoint uses the content of register
+ ERP as it is whereas a dynamic breakpoint requires subtraction with 2
+ in order to execute the instruction. The first breakpoint is static; all
+ following are assumed to be dynamic. */
+static int dynamic_bp = 0;
+
+/********************************* String library ****************************/
+/* Single-step over library functions creates trap loops. */
+
+/* Copy char s2[] to s1[]. */
+static char*
+gdb_cris_strcpy(char *s1, const char *s2)
+{
+ char *s = s1;
+
+ for (s = s1; (*s++ = *s2++) != '\0'; )
+ ;
+ return s1;
+}
+
+/* Find length of s[]. */
+static int
+gdb_cris_strlen(const char *s)
+{
+ const char *sc;
+
+ for (sc = s; *sc != '\0'; sc++)
+ ;
+ return (sc - s);
+}
+
+/* Find first occurrence of c in s[n]. */
+static void*
+gdb_cris_memchr(const void *s, int c, int n)
+{
+ const unsigned char uc = c;
+ const unsigned char *su;
+
+ for (su = s; 0 < n; ++su, --n)
+ if (*su == uc)
+ return (void *)su;
+ return NULL;
+}
+/******************************* Standard library ****************************/
+/* Single-step over library functions creates trap loops. */
+/* Convert string to long. */
+static int
+gdb_cris_strtol(const char *s, char **endptr, int base)
+{
+ char *s1;
+ char *sd;
+ int x = 0;
+
+ for (s1 = (char*)s; (sd = gdb_cris_memchr(hex_asc, *s1, base)) != NULL; ++s1)
+ x = x * base + (sd - hex_asc);
+
+ if (endptr) {
+ /* Unconverted suffix is stored in endptr unless endptr is NULL. */
+ *endptr = s1;
+ }
+
+ return x;
+}
+
+/********************************* Register image ****************************/
+
+/* Write a value to a specified register in the register image of the current
+ thread. Returns status code SUCCESS, E02 or E05. */
+static int
+write_register(int regno, char *val)
+{
+ int status = SUCCESS;
+
+ if (regno >= R0 && regno <= ACR) {
+ /* Consecutive 32-bit registers. */
+ hex2mem((unsigned char *)&reg.r0 + (regno - R0) * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+
+ } else if (regno == BZ || regno == VR || regno == WZ || regno == DZ) {
+ /* Read-only registers. */
+ status = E02;
+
+ } else if (regno == PID) {
+ /* 32-bit register. (Even though we already checked SRS and WZ, we cannot
+ combine this with the EXS - SPC write since SRS and WZ have different size.) */
+ hex2mem((unsigned char *)&reg.pid, val, sizeof(unsigned int));
+
+ } else if (regno == SRS) {
+ /* 8-bit register. */
+ hex2mem((unsigned char *)&reg.srs, val, sizeof(unsigned char));
+
+ } else if (regno >= EXS && regno <= SPC) {
+ /* Consecutive 32-bit registers. */
+ hex2mem((unsigned char *)&reg.exs + (regno - EXS) * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+
+ } else if (regno == PC) {
+ /* Pseudo-register. Treat as read-only. */
+ status = E02;
+
+ } else if (regno >= S0 && regno <= S15) {
+ /* 32-bit registers. */
+ hex2mem((unsigned char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int), val, sizeof(unsigned int));
+ } else {
+ /* Non-existing register. */
+ status = E05;
+ }
+ return status;
+}
+
+/* Read a value from a specified register in the register image. Returns the
+ value in the register or -1 for non-implemented registers. */
+static int
+read_register(char regno, unsigned int *valptr)
+{
+ int status = SUCCESS;
+
+ /* We read the zero registers from the register struct (instead of just returning 0)
+ to catch errors. */
+
+ if (regno >= R0 && regno <= ACR) {
+ /* Consecutive 32-bit registers. */
+ *valptr = *(unsigned int *)((char *)&reg.r0 + (regno - R0) * sizeof(unsigned int));
+
+ } else if (regno == BZ || regno == VR) {
+ /* Consecutive 8-bit registers. */
+ *valptr = (unsigned int)(*(unsigned char *)
+ ((char *)&reg.bz + (regno - BZ) * sizeof(char)));
+
+ } else if (regno == PID) {
+ /* 32-bit register. */
+ *valptr = *(unsigned int *)((char *)&reg.pid);
+
+ } else if (regno == SRS) {
+ /* 8-bit register. */
+ *valptr = (unsigned int)(*(unsigned char *)((char *)&reg.srs));
+
+ } else if (regno == WZ) {
+ /* 16-bit register. */
+ *valptr = (unsigned int)(*(unsigned short *)(char *)&reg.wz);
+
+ } else if (regno >= EXS && regno <= PC) {
+ /* Consecutive 32-bit registers. */
+ *valptr = *(unsigned int *)((char *)&reg.exs + (regno - EXS) * sizeof(unsigned int));
+
+ } else if (regno >= S0 && regno <= S15) {
+ /* Consecutive 32-bit registers, located elsewhere. */
+ *valptr = *(unsigned int *)((char *)&sreg.s0_0 + (reg.srs * 16 * sizeof(unsigned int)) + (regno - S0) * sizeof(unsigned int));
+
+ } else {
+ /* Non-existing register. */
+ status = E05;
+ }
+ return status;
+
+}
+
+/********************************** Packet I/O ******************************/
+/* Returns the integer equivalent of a hexadecimal character. */
+static int
+hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f'))
+ return (ch - 'a' + 10);
+ if ((ch >= '0') && (ch <= '9'))
+ return (ch - '0');
+ if ((ch >= 'A') && (ch <= 'F'))
+ return (ch - 'A' + 10);
+ return -1;
+}
+
+/* Convert the memory, pointed to by mem into hexadecimal representation.
+ Put the result in buf, and return a pointer to the last character
+ in buf (null). */
+
+static char *
+mem2hex(char *buf, unsigned char *mem, int count)
+{
+ int i;
+ int ch;
+
+ if (mem == NULL) {
+ /* Invalid address, caught by 'm' packet handler. */
+ for (i = 0; i < count; i++) {
+ *buf++ = '0';
+ *buf++ = '0';
+ }
+ } else {
+ /* Valid mem address. */
+ for (i = 0; i < count; i++) {
+ ch = *mem++;
+ buf = hex_byte_pack(buf, ch);
+ }
+ }
+ /* Terminate properly. */
+ *buf = '\0';
+ return buf;
+}
+
+/* Same as mem2hex, but puts it in network byte order. */
+static char *
+mem2hex_nbo(char *buf, unsigned char *mem, int count)
+{
+ int i;
+ int ch;
+
+ mem += count - 1;
+ for (i = 0; i < count; i++) {
+ ch = *mem--;
+ buf = hex_byte_pack(buf, ch);
+ }
+
+ /* Terminate properly. */
+ *buf = '\0';
+ return buf;
+}
+
+/* Convert the array, in hexadecimal representation, pointed to by buf into
+ binary representation. Put the result in mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char*
+hex2mem(unsigned char *mem, char *buf, int count)
+{
+ int i;
+ unsigned char ch;
+ for (i = 0; i < count; i++) {
+ ch = hex (*buf++) << 4;
+ ch = ch + hex (*buf++);
+ *mem++ = ch;
+ }
+ return mem;
+}
+
+/* Put the content of the array, in binary representation, pointed to by buf
+ into memory pointed to by mem, and return a pointer to the character after
+ the last byte written.
+ Gdb will escape $, #, and the escape char (0x7d). */
+static unsigned char*
+bin2mem(unsigned char *mem, unsigned char *buf, int count)
+{
+ int i;
+ unsigned char *next;
+ for (i = 0; i < count; i++) {
+ /* Check for any escaped characters. Be paranoid and
+ only unescape chars that should be escaped. */
+ if (*buf == 0x7d) {
+ next = buf + 1;
+ if (*next == 0x3 || *next == 0x4 || *next == 0x5D) {
+ /* #, $, ESC */
+ buf++;
+ *buf += 0x20;
+ }
+ }
+ *mem++ = *buf++;
+ }
+ return mem;
+}
+
+/* Await the sequence $<data>#<checksum> and store <data> in the array buffer
+ returned. */
+static void
+getpacket(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ char ch;
+
+ do {
+ while((ch = getDebugChar ()) != '$')
+ /* Wait for the start character $ and ignore all other characters */;
+ checksum = 0;
+ xmitcsum = -1;
+ count = 0;
+ /* Read until a # or the end of the buffer is reached */
+ while (count < BUFMAX) {
+ ch = getDebugChar();
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+
+ if (count >= BUFMAX)
+ continue;
+
+ buffer[count] = 0;
+
+ if (ch == '#') {
+ xmitcsum = hex(getDebugChar()) << 4;
+ xmitcsum += hex(getDebugChar());
+ if (checksum != xmitcsum) {
+ /* Wrong checksum */
+ putDebugChar('-');
+ } else {
+ /* Correct checksum */
+ putDebugChar('+');
+ /* If sequence characters are received, reply with them */
+ if (buffer[2] == ':') {
+ putDebugChar(buffer[0]);
+ putDebugChar(buffer[1]);
+ /* Remove the sequence characters from the buffer */
+ count = gdb_cris_strlen(buffer);
+ for (i = 3; i <= count; i++)
+ buffer[i - 3] = buffer[i];
+ }
+ }
+ }
+ } while (checksum != xmitcsum);
+}
+
+/* Send $<data>#<checksum> from the <data> in the array buffer. */
+
+static void
+putpacket(char *buffer)
+{
+ int checksum;
+ int runlen;
+ int encode;
+
+ do {
+ char *src = buffer;
+ putDebugChar('$');
+ checksum = 0;
+ while (*src) {
+ /* Do run length encoding */
+ putDebugChar(*src);
+ checksum += *src;
+ runlen = 0;
+ while (runlen < RUNLENMAX && *src == src[runlen]) {
+ runlen++;
+ }
+ if (runlen > 3) {
+ /* Got a useful amount */
+ putDebugChar ('*');
+ checksum += '*';
+ encode = runlen + ' ' - 4;
+ putDebugChar(encode);
+ checksum += encode;
+ src += runlen;
+ } else {
+ src++;
+ }
+ }
+ putDebugChar('#');
+ putDebugChar(hex_asc_hi(checksum));
+ putDebugChar(hex_asc_lo(checksum));
+ } while(kgdb_started && (getDebugChar() != '+'));
+}
+
+/* The string str is prepended with the GDB printout token and sent. Required
+ in traditional implementations. */
+void
+putDebugString(const unsigned char *str, int len)
+{
+ /* Move SPC forward if we are single-stepping. */
+ asm("spchere:");
+ asm("move $spc, $r10");
+ asm("cmp.d spchere, $r10");
+ asm("bne nosstep");
+ asm("nop");
+ asm("move.d spccont, $r10");
+ asm("move $r10, $spc");
+ asm("nosstep:");
+
+ output_buffer[0] = 'O';
+ mem2hex(&output_buffer[1], (unsigned char *)str, len);
+ putpacket(output_buffer);
+
+ asm("spccont:");
+}
+
+/********************************** Handle exceptions ************************/
+/* Build and send a response packet in order to inform the host the
+ stub is stopped. TAAn...:r...;n...:r...;n...:r...;
+ AA = signal number
+ n... = register number (hex)
+ r... = register contents
+ n... = `thread'
+ r... = thread process ID. This is a hex integer.
+ n... = other string not starting with valid hex digit.
+ gdb should ignore this n,r pair and go on to the next.
+ This way we can extend the protocol. */
+static void
+stub_is_stopped(int sigval)
+{
+ char *ptr = output_buffer;
+ unsigned int reg_cont;
+
+ /* Send trap type (converted to signal) */
+
+ *ptr++ = 'T';
+ ptr = hex_byte_pack(ptr, sigval);
+
+ if (((reg.exs & 0xff00) >> 8) == 0xc) {
+
+ /* Some kind of hardware watchpoint triggered. Find which one
+ and determine its type (read/write/access). */
+ int S, bp, trig_bits = 0, rw_bits = 0;
+ int trig_mask = 0;
+ unsigned int *bp_d_regs = &sreg.s3_3;
+ /* In a lot of cases, the stopped data address will simply be EDA.
+ In some cases, we adjust it to match the watched data range.
+ (We don't want to change the actual EDA though). */
+ unsigned int stopped_data_address;
+ /* The S field of EXS. */
+ S = (reg.exs & 0xffff0000) >> 16;
+
+ if (S & 1) {
+ /* Instruction watchpoint. */
+ /* FIXME: Check against, and possibly adjust reported EDA. */
+ } else {
+ /* Data watchpoint. Find the one that triggered. */
+ for (bp = 0; bp < 6; bp++) {
+
+ /* Dx_RD, Dx_WR in the S field of EXS for this BP. */
+ int bitpos_trig = 1 + bp * 2;
+ /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */
+ int bitpos_config = 2 + bp * 4;
+
+ /* Get read/write trig bits for this BP. */
+ trig_bits = (S & (3 << bitpos_trig)) >> bitpos_trig;
+
+ /* Read/write config bits for this BP. */
+ rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config;
+ if (trig_bits) {
+ /* Sanity check: the BP shouldn't trigger for accesses
+ that it isn't configured for. */
+ if ((rw_bits == 0x1 && trig_bits != 0x1) ||
+ (rw_bits == 0x2 && trig_bits != 0x2))
+ panic("Invalid r/w trigging for this BP");
+
+ /* Mark this BP as trigged for future reference. */
+ trig_mask |= (1 << bp);
+
+ if (reg.eda >= bp_d_regs[bp * 2] &&
+ reg.eda <= bp_d_regs[bp * 2 + 1]) {
+ /* EDA within range for this BP; it must be the one
+ we're looking for. */
+ stopped_data_address = reg.eda;
+ break;
+ }
+ }
+ }
+ if (bp < 6) {
+ /* Found a trigged BP with EDA within its configured data range. */
+ } else if (trig_mask) {
+ /* Something triggered, but EDA doesn't match any BP's range. */
+ for (bp = 0; bp < 6; bp++) {
+ /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */
+ int bitpos_config = 2 + bp * 4;
+
+ /* Read/write config bits for this BP (needed later). */
+ rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config;
+
+ if (trig_mask & (1 << bp)) {
+ /* EDA within 31 bytes of the configured start address? */
+ if (reg.eda + 31 >= bp_d_regs[bp * 2]) {
+ /* Changing the reported address to match
+ the start address of the first applicable BP. */
+ stopped_data_address = bp_d_regs[bp * 2];
+ break;
+ } else {
+ /* We continue since we might find another useful BP. */
+ printk("EDA doesn't match trigged BP's range");
+ }
+ }
+ }
+ }
+
+ /* No match yet? */
+ BUG_ON(bp >= 6);
+ /* Note that we report the type according to what the BP is configured
+ for (otherwise we'd never report an 'awatch'), not according to how
+ it trigged. We did check that the trigged bits match what the BP is
+ configured for though. */
+ if (rw_bits == 0x1) {
+ /* read */
+ strncpy(ptr, "rwatch", 6);
+ ptr += 6;
+ } else if (rw_bits == 0x2) {
+ /* write */
+ strncpy(ptr, "watch", 5);
+ ptr += 5;
+ } else if (rw_bits == 0x3) {
+ /* access */
+ strncpy(ptr, "awatch", 6);
+ ptr += 6;
+ } else {
+ panic("Invalid r/w bits for this BP.");
+ }
+
+ *ptr++ = ':';
+ /* Note that we don't read_register(EDA, ...) */
+ ptr = mem2hex_nbo(ptr, (unsigned char *)&stopped_data_address, register_size[EDA]);
+ *ptr++ = ';';
+ }
+ }
+ /* Only send PC, frame and stack pointer. */
+ read_register(PC, &reg_cont);
+ ptr = hex_byte_pack(ptr, PC);
+ *ptr++ = ':';
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont, register_size[PC]);
+ *ptr++ = ';';
+
+ read_register(R8, &reg_cont);
+ ptr = hex_byte_pack(ptr, R8);
+ *ptr++ = ':';
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont, register_size[R8]);
+ *ptr++ = ';';
+
+ read_register(SP, &reg_cont);
+ ptr = hex_byte_pack(ptr, SP);
+ *ptr++ = ':';
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont, register_size[SP]);
+ *ptr++ = ';';
+
+ /* Send ERP as well; this will save us an entire register fetch in some cases. */
+ read_register(ERP, &reg_cont);
+ ptr = hex_byte_pack(ptr, ERP);
+ *ptr++ = ':';
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont, register_size[ERP]);
+ *ptr++ = ';';
+
+ /* null-terminate and send it off */
+ *ptr = 0;
+ putpacket(output_buffer);
+}
+
+/* Returns the size of an instruction that has a delay slot. */
+
+int insn_size(unsigned long pc)
+{
+ unsigned short opcode = *(unsigned short *)pc;
+ int size = 0;
+
+ switch ((opcode & 0x0f00) >> 8) {
+ case 0x0:
+ case 0x9:
+ case 0xb:
+ size = 2;
+ break;
+ case 0xe:
+ case 0xf:
+ size = 6;
+ break;
+ case 0xd:
+ /* Could be 4 or 6; check more bits. */
+ if ((opcode & 0xff) == 0xff)
+ size = 4;
+ else
+ size = 6;
+ break;
+ default:
+ panic("Couldn't find size of opcode 0x%x at 0x%lx\n", opcode, pc);
+ }
+
+ return size;
+}
+
+void register_fixup(int sigval)
+{
+ /* Compensate for ACR push at the beginning of exception handler. */
+ reg.sp += 4;
+
+ /* Standard case. */
+ reg.pc = reg.erp;
+ if (reg.erp & 0x1) {
+ /* Delay slot bit set. Report as stopped on proper instruction. */
+ if (reg.spc) {
+ /* Rely on SPC if set. */
+ reg.pc = reg.spc;
+ } else {
+ /* Calculate the PC from the size of the instruction
+ that the delay slot we're in belongs to. */
+ reg.pc += insn_size(reg.erp & ~1) - 1 ;
+ }
+ }
+
+ if ((reg.exs & 0x3) == 0x0) {
+ /* Bits 1 - 0 indicate the type of memory operation performed
+ by the interrupted instruction. 0 means no memory operation,
+ and EDA is undefined in that case. We zero it to avoid confusion. */
+ reg.eda = 0;
+ }
+
+ if (sigval == SIGTRAP) {
+ /* Break 8, single step or hardware breakpoint exception. */
+
+ /* Check IDX field of EXS. */
+ if (((reg.exs & 0xff00) >> 8) == 0x18) {
+
+ /* Break 8. */
+
+ /* Static (compiled) breakpoints must return to the next instruction
+ in order to avoid infinite loops (default value of ERP). Dynamic
+ (gdb-invoked) must subtract the size of the break instruction from
+ the ERP so that the instruction that was originally in the break
+ instruction's place will be run when we return from the exception. */
+ if (!dynamic_bp) {
+ /* Assuming that all breakpoints are dynamic from now on. */
+ dynamic_bp = 1;
+ } else {
+
+ /* Only if not in a delay slot. */
+ if (!(reg.erp & 0x1)) {
+ reg.erp -= 2;
+ reg.pc -= 2;
+ }
+ }
+
+ } else if (((reg.exs & 0xff00) >> 8) == 0x3) {
+ /* Single step. */
+ /* Don't fiddle with S1. */
+
+ } else if (((reg.exs & 0xff00) >> 8) == 0xc) {
+
+ /* Hardware watchpoint exception. */
+
+ /* SPC has been updated so that we will get a single step exception
+ when we return, but we don't want that. */
+ reg.spc = 0;
+
+ /* Don't fiddle with S1. */
+ }
+
+ } else if (sigval == SIGINT) {
+ /* Nothing special. */
+ }
+}
+
+static void insert_watchpoint(char type, int addr, int len)
+{
+ /* Breakpoint/watchpoint types (GDB terminology):
+ 0 = memory breakpoint for instructions
+ (not supported; done via memory write instead)
+ 1 = hardware breakpoint for instructions (supported)
+ 2 = write watchpoint (supported)
+ 3 = read watchpoint (supported)
+ 4 = access watchpoint (supported) */
+
+ if (type < '1' || type > '4') {
+ output_buffer[0] = 0;
+ return;
+ }
+
+ /* Read watchpoints are set as access watchpoints, because of GDB's
+ inability to deal with pure read watchpoints. */
+ if (type == '3')
+ type = '4';
+
+ if (type == '1') {
+ /* Hardware (instruction) breakpoint. */
+ /* Bit 0 in BP_CTRL holds the configuration for I0. */
+ if (sreg.s0_3 & 0x1) {
+ /* Already in use. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ return;
+ }
+ /* Configure. */
+ sreg.s1_3 = addr;
+ sreg.s2_3 = (addr + len - 1);
+ sreg.s0_3 |= 1;
+ } else {
+ int bp;
+ unsigned int *bp_d_regs = &sreg.s3_3;
+
+ /* The watchpoint allocation scheme is the simplest possible.
+ For example, if a region is watched for read and
+ a write watch is requested, a new watchpoint will
+ be used. Also, if a watch for a region that is already
+ covered by one or more existing watchpoints, a new
+ watchpoint will be used. */
+
+ /* First, find a free data watchpoint. */
+ for (bp = 0; bp < 6; bp++) {
+ /* Each data watchpoint's control registers occupy 2 bits
+ (hence the 3), starting at bit 2 for D0 (hence the 2)
+ with 4 bits between for each watchpoint (yes, the 4). */
+ if (!(sreg.s0_3 & (0x3 << (2 + (bp * 4))))) {
+ break;
+ }
+ }
+
+ if (bp > 5) {
+ /* We're out of watchpoints. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ return;
+ }
+
+ /* Configure the control register first. */
+ if (type == '3' || type == '4') {
+ /* Trigger on read. */
+ sreg.s0_3 |= (1 << (2 + bp * 4));
+ }
+ if (type == '2' || type == '4') {
+ /* Trigger on write. */
+ sreg.s0_3 |= (2 << (2 + bp * 4));
+ }
+
+ /* Ugly pointer arithmetics to configure the watched range. */
+ bp_d_regs[bp * 2] = addr;
+ bp_d_regs[bp * 2 + 1] = (addr + len - 1);
+ }
+
+ /* Set the S1 flag to enable watchpoints. */
+ reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+ gdb_cris_strcpy(output_buffer, "OK");
+}
+
+static void remove_watchpoint(char type, int addr, int len)
+{
+ /* Breakpoint/watchpoint types:
+ 0 = memory breakpoint for instructions
+ (not supported; done via memory write instead)
+ 1 = hardware breakpoint for instructions (supported)
+ 2 = write watchpoint (supported)
+ 3 = read watchpoint (supported)
+ 4 = access watchpoint (supported) */
+ if (type < '1' || type > '4') {
+ output_buffer[0] = 0;
+ return;
+ }
+
+ /* Read watchpoints are set as access watchpoints, because of GDB's
+ inability to deal with pure read watchpoints. */
+ if (type == '3')
+ type = '4';
+
+ if (type == '1') {
+ /* Hardware breakpoint. */
+ /* Bit 0 in BP_CTRL holds the configuration for I0. */
+ if (!(sreg.s0_3 & 0x1)) {
+ /* Not in use. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ return;
+ }
+ /* Deconfigure. */
+ sreg.s1_3 = 0;
+ sreg.s2_3 = 0;
+ sreg.s0_3 &= ~1;
+ } else {
+ int bp;
+ unsigned int *bp_d_regs = &sreg.s3_3;
+ /* Try to find a watchpoint that is configured for the
+ specified range, then check that read/write also matches. */
+
+ /* Ugly pointer arithmetic, since I cannot rely on a
+ single switch (addr) as there may be several watchpoints with
+ the same start address for example. */
+
+ for (bp = 0; bp < 6; bp++) {
+ if (bp_d_regs[bp * 2] == addr &&
+ bp_d_regs[bp * 2 + 1] == (addr + len - 1)) {
+ /* Matching range. */
+ int bitpos = 2 + bp * 4;
+ int rw_bits;
+
+ /* Read/write bits for this BP. */
+ rw_bits = (sreg.s0_3 & (0x3 << bitpos)) >> bitpos;
+
+ if ((type == '3' && rw_bits == 0x1) ||
+ (type == '2' && rw_bits == 0x2) ||
+ (type == '4' && rw_bits == 0x3)) {
+ /* Read/write matched. */
+ break;
+ }
+ }
+ }
+
+ if (bp > 5) {
+ /* No watchpoint matched. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ return;
+ }
+
+ /* Found a matching watchpoint. Now, deconfigure it by
+ both disabling read/write in bp_ctrl and zeroing its
+ start/end addresses. */
+ sreg.s0_3 &= ~(3 << (2 + (bp * 4)));
+ bp_d_regs[bp * 2] = 0;
+ bp_d_regs[bp * 2 + 1] = 0;
+ }
+
+ /* Note that we don't clear the S1 flag here. It's done when continuing. */
+ gdb_cris_strcpy(output_buffer, "OK");
+}
+
+
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. */
+void
+handle_exception(int sigval)
+{
+ /* Avoid warning of not used. */
+
+ USEDFUN(handle_exception);
+ USEDVAR(internal_stack[0]);
+
+ register_fixup(sigval);
+
+ /* Send response. */
+ stub_is_stopped(sigval);
+
+ for (;;) {
+ output_buffer[0] = '\0';
+ getpacket(input_buffer);
+ switch (input_buffer[0]) {
+ case 'g':
+ /* Read registers: g
+ Success: Each byte of register data is described by two hex digits.
+ Registers are in the internal order for GDB, and the bytes
+ in a register are in the same order the machine uses.
+ Failure: void. */
+ {
+ char *buf;
+ /* General and special registers. */
+ buf = mem2hex(output_buffer, (char *)&reg, sizeof(registers));
+ /* Support registers. */
+ /* -1 because of the null termination that mem2hex adds. */
+ mem2hex(buf,
+ (char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
+ 16 * sizeof(unsigned int));
+ break;
+ }
+ case 'G':
+ /* Write registers. GXX..XX
+ Each byte of register data is described by two hex digits.
+ Success: OK
+ Failure: void. */
+ /* General and special registers. */
+ hex2mem((char *)&reg, &input_buffer[1], sizeof(registers));
+ /* Support registers. */
+ hex2mem((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
+ &input_buffer[1] + sizeof(registers),
+ 16 * sizeof(unsigned int));
+ gdb_cris_strcpy(output_buffer, "OK");
+ break;
+
+ case 'P':
+ /* Write register. Pn...=r...
+ Write register n..., hex value without 0x, with value r...,
+ which contains a hex value without 0x and two hex digits
+ for each byte in the register (target byte order). P1f=11223344 means
+ set register 31 to 44332211.
+ Success: OK
+ Failure: E02, E05 */
+ {
+ char *suffix;
+ int regno = gdb_cris_strtol(&input_buffer[1], &suffix, 16);
+ int status;
+
+ status = write_register(regno, suffix+1);
+
+ switch (status) {
+ case E02:
+ /* Do not support read-only registers. */
+ gdb_cris_strcpy(output_buffer, error_message[E02]);
+ break;
+ case E05:
+ /* Do not support non-existing registers. */
+ gdb_cris_strcpy(output_buffer, error_message[E05]);
+ break;
+ default:
+ /* Valid register number. */
+ gdb_cris_strcpy(output_buffer, "OK");
+ break;
+ }
+ }
+ break;
+
+ case 'm':
+ /* Read from memory. mAA..AA,LLLL
+ AA..AA is the address and LLLL is the length.
+ Success: XX..XX is the memory content. Can be fewer bytes than
+ requested if only part of the data may be read. m6000120a,6c means
+ retrieve 108 byte from base address 6000120a.
+ Failure: void. */
+ {
+ char *suffix;
+ unsigned char *addr = (unsigned char *)gdb_cris_strtol(&input_buffer[1],
+ &suffix, 16);
+ int len = gdb_cris_strtol(suffix+1, 0, 16);
+
+ /* Bogus read (i.e. outside the kernel's
+ segment)? . */
+ if (!((unsigned int)addr >= 0xc0000000 &&
+ (unsigned int)addr < 0xd0000000))
+ addr = NULL;
+
+ mem2hex(output_buffer, addr, len);
+ }
+ break;
+
+ case 'X':
+ /* Write to memory. XAA..AA,LLLL:XX..XX
+ AA..AA is the start address, LLLL is the number of bytes, and
+ XX..XX is the binary data.
+ Success: OK
+ Failure: void. */
+ case 'M':
+ /* Write to memory. MAA..AA,LLLL:XX..XX
+ AA..AA is the start address, LLLL is the number of bytes, and
+ XX..XX is the hexadecimal data.
+ Success: OK
+ Failure: void. */
+ {
+ char *lenptr;
+ char *dataptr;
+ unsigned char *addr = (unsigned char *)gdb_cris_strtol(&input_buffer[1],
+ &lenptr, 16);
+ int len = gdb_cris_strtol(lenptr+1, &dataptr, 16);
+ if (*lenptr == ',' && *dataptr == ':') {
+ if (input_buffer[0] == 'M') {
+ hex2mem(addr, dataptr + 1, len);
+ } else /* X */ {
+ bin2mem(addr, dataptr + 1, len);
+ }
+ gdb_cris_strcpy(output_buffer, "OK");
+ }
+ else {
+ gdb_cris_strcpy(output_buffer, error_message[E06]);
+ }
+ }
+ break;
+
+ case 'c':
+ /* Continue execution. cAA..AA
+ AA..AA is the address where execution is resumed. If AA..AA is
+ omitted, resume at the present address.
+ Success: return to the executing thread.
+ Failure: will never know. */
+
+ if (input_buffer[1] != '\0') {
+ /* FIXME: Doesn't handle address argument. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ break;
+ }
+
+ /* Before continuing, make sure everything is set up correctly. */
+
+ /* Set the SPC to some unlikely value. */
+ reg.spc = 0;
+ /* Set the S1 flag to 0 unless some watchpoint is enabled (since setting
+ S1 to 0 would also disable watchpoints). (Note that bits 26-31 in BP_CTRL
+ are reserved, so don't check against those). */
+ if ((sreg.s0_3 & 0x3fff) == 0) {
+ reg.ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
+ }
+
+ return;
+
+ case 's':
+ /* Step. sAA..AA
+ AA..AA is the address where execution is resumed. If AA..AA is
+ omitted, resume at the present address. Success: return to the
+ executing thread. Failure: will never know. */
+
+ if (input_buffer[1] != '\0') {
+ /* FIXME: Doesn't handle address argument. */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ break;
+ }
+
+ /* Set the SPC to PC, which is where we'll return
+ (deduced previously). */
+ reg.spc = reg.pc;
+
+ /* Set the S1 (first stacked, not current) flag, which will
+ kick into action when we rfe. */
+ reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+ return;
+
+ case 'Z':
+
+ /* Insert breakpoint or watchpoint, Ztype,addr,length.
+ Remote protocol says: A remote target shall return an empty string
+ for an unrecognized breakpoint or watchpoint packet type. */
+ {
+ char *lenptr;
+ char *dataptr;
+ int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16);
+ int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16);
+ char type = input_buffer[1];
+
+ insert_watchpoint(type, addr, len);
+ break;
+ }
+
+ case 'z':
+ /* Remove breakpoint or watchpoint, Ztype,addr,length.
+ Remote protocol says: A remote target shall return an empty string
+ for an unrecognized breakpoint or watchpoint packet type. */
+ {
+ char *lenptr;
+ char *dataptr;
+ int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16);
+ int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16);
+ char type = input_buffer[1];
+
+ remove_watchpoint(type, addr, len);
+ break;
+ }
+
+
+ case '?':
+ /* The last signal which caused a stop. ?
+ Success: SAA, where AA is the signal number.
+ Failure: void. */
+ output_buffer[0] = 'S';
+ output_buffer[1] = hex_asc_hi(sigval);
+ output_buffer[2] = hex_asc_lo(sigval);
+ output_buffer[3] = 0;
+ break;
+
+ case 'D':
+ /* Detach from host. D
+ Success: OK, and return to the executing thread.
+ Failure: will never know */
+ putpacket("OK");
+ return;
+
+ case 'k':
+ case 'r':
+ /* kill request or reset request.
+ Success: restart of target.
+ Failure: will never know. */
+ kill_restart();
+ break;
+
+ case 'C':
+ case 'S':
+ case '!':
+ case 'R':
+ case 'd':
+ /* Continue with signal sig. Csig;AA..AA
+ Step with signal sig. Ssig;AA..AA
+ Use the extended remote protocol. !
+ Restart the target system. R0
+ Toggle debug flag. d
+ Search backwards. tAA:PP,MM
+ Not supported: E04 */
+
+ /* FIXME: What's the difference between not supported
+ and ignored (below)? */
+ gdb_cris_strcpy(output_buffer, error_message[E04]);
+ break;
+
+ default:
+ /* The stub should ignore other request and send an empty
+ response ($#<checksum>). This way we can extend the protocol and GDB
+ can tell whether the stub it is talking to uses the old or the new. */
+ output_buffer[0] = 0;
+ break;
+ }
+ putpacket(output_buffer);
+ }
+}
+
+void
+kgdb_init(void)
+{
+ reg_intr_vect_rw_mask intr_mask;
+ reg_ser_rw_intr_mask ser_intr_mask;
+
+ /* Configure the kgdb serial port. */
+#if defined(CONFIG_ETRAX_KGDB_PORT0)
+ /* Note: no shortcut registered (not handled by multiple_interrupt).
+ See entry.S. */
+ set_exception_vector(SER0_INTR_VECT, kgdb_handle_exception);
+ /* Enable the ser irq in the global config. */
+ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+ intr_mask.ser0 = 1;
+ REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+ ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask);
+ ser_intr_mask.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).
+ See entry.S. */
+ set_exception_vector(SER1_INTR_VECT, kgdb_handle_exception);
+ /* Enable the ser irq in the global config. */
+ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+ intr_mask.ser1 = 1;
+ REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+ ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask);
+ ser_intr_mask.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).
+ See entry.S. */
+ set_exception_vector(SER2_INTR_VECT, kgdb_handle_exception);
+ /* Enable the ser irq in the global config. */
+ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+ intr_mask.ser2 = 1;
+ REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+ ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask);
+ ser_intr_mask.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).
+ See entry.S. */
+ set_exception_vector(SER3_INTR_VECT, kgdb_handle_exception);
+ /* Enable the ser irq in the global config. */
+ intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+ intr_mask.ser3 = 1;
+ REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+ ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask);
+ ser_intr_mask.dav = regk_ser_yes;
+ REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask);
+#endif
+
+}
+/* Performs a complete re-start from scratch. */
+static void
+kill_restart(void)
+{
+ machine_restart("");
+}
+
+/* Use this static breakpoint in the start-up only. */
+
+void
+breakpoint(void)
+{
+ kgdb_started = 1;
+ dynamic_bp = 0; /* This is a static, not a dynamic breakpoint. */
+ __asm__ volatile ("break 8"); /* Jump to kgdb_handle_breakpoint. */
+}
+
+/****************************** End of file **********************************/
diff --git a/arch/cris/arch-v32/kernel/kgdb_asm.S b/arch/cris/arch-v32/kernel/kgdb_asm.S
new file mode 100644
index 000000000..f3a476059
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/kgdb_asm.S
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2004 Axis Communications AB
+ *
+ * Code for handling break 8, hardware breakpoint, single step, and serial
+ * port exceptions for kernel debugging purposes.
+ */
+
+#include <hwregs/intr_vect.h>
+
+ ;; Exported functions.
+ .globl kgdb_handle_exception
+
+kgdb_handle_exception:
+
+;; Create a register image of the caller.
+;;
+;; First of all, save the ACR on the stack since we need it for address calculations.
+;; We put it into the register struct later.
+
+ subq 4, $sp
+ move.d $acr, [$sp]
+
+;; Now we are free to use ACR all we want.
+;; If we were running this handler with interrupts on, we would have to be careful
+;; to save and restore CCS manually, but since we aren't we treat it like every other
+;; register.
+
+ move.d reg, $acr
+ move.d $r0, [$acr] ; Save R0 (start of register struct)
+ addq 4, $acr
+ move.d $r1, [$acr] ; Save R1
+ addq 4, $acr
+ move.d $r2, [$acr] ; Save R2
+ addq 4, $acr
+ move.d $r3, [$acr] ; Save R3
+ addq 4, $acr
+ move.d $r4, [$acr] ; Save R4
+ addq 4, $acr
+ move.d $r5, [$acr] ; Save R5
+ addq 4, $acr
+ move.d $r6, [$acr] ; Save R6
+ addq 4, $acr
+ move.d $r7, [$acr] ; Save R7
+ addq 4, $acr
+ move.d $r8, [$acr] ; Save R8
+ addq 4, $acr
+ move.d $r9, [$acr] ; Save R9
+ addq 4, $acr
+ move.d $r10, [$acr] ; Save R10
+ addq 4, $acr
+ move.d $r11, [$acr] ; Save R11
+ addq 4, $acr
+ move.d $r12, [$acr] ; Save R12
+ addq 4, $acr
+ move.d $r13, [$acr] ; Save R13
+ addq 4, $acr
+ move.d $sp, [$acr] ; Save SP (R14)
+ addq 4, $acr
+
+ ;; The ACR register is already saved on the stack, so pop it from there.
+ move.d [$sp],$r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+
+ move $bz, [$acr]
+ addq 1, $acr
+ move $vr, [$acr]
+ addq 1, $acr
+ move $pid, [$acr]
+ addq 4, $acr
+ move $srs, [$acr]
+ addq 1, $acr
+ move $wz, [$acr]
+ addq 2, $acr
+ move $exs, [$acr]
+ addq 4, $acr
+ move $eda, [$acr]
+ addq 4, $acr
+ move $mof, [$acr]
+ addq 4, $acr
+ move $dz, [$acr]
+ addq 4, $acr
+ move $ebp, [$acr]
+ addq 4, $acr
+ move $erp, [$acr]
+ addq 4, $acr
+ move $srp, [$acr]
+ addq 4, $acr
+ move $nrp, [$acr]
+ addq 4, $acr
+ move $ccs, [$acr]
+ addq 4, $acr
+ move $usp, [$acr]
+ addq 4, $acr
+ move $spc, [$acr]
+ addq 4, $acr
+
+;; Skip the pseudo-PC.
+ addq 4, $acr
+
+;; Save the support registers in bank 0 - 3.
+ clear.d $r1 ; Bank counter
+ move.d sreg, $acr
+
+;; Bank 0
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move $s0, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s1, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s2, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s3, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s4, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s5, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s6, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s7, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s8, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s9, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s10, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s11, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s12, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+
+ ;; Nothing in S13 - S15, bank 0
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+
+;; Bank 1 and bank 2 have the same layout, hence the loop.
+ addq 1, $r1
+1:
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move $s0, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s1, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s2, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s3, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s4, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s5, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s6, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+
+ ;; Nothing in S7 - S15, bank 1 and 2
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+ clear.d [$acr]
+ addq 4, $acr
+
+ addq 1, $r1
+ cmpq 3, $r1
+ bne 1b
+ nop
+
+;; Bank 3
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move $s0, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s1, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s2, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s3, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s4, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s5, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s6, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s7, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s8, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s9, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s10, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s11, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s12, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s13, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+ move $s14, $r0
+ move.d $r0, [$acr]
+ addq 4, $acr
+;; Nothing in S15, bank 3
+ clear.d [$acr]
+ addq 4, $acr
+
+;; Check what got us here: get IDX field of EXS.
+ move $exs, $r10
+ and.d 0xff00, $r10
+ lsrq 8, $r10
+#if defined(CONFIG_ETRAX_KGDB_PORT0)
+ cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0
+ beq sigint
+ nop
+#elif defined(CONFIG_ETRAX_KGDB_PORT1)
+ cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1
+ beq sigint
+ nop
+#elif defined(CONFIG_ETRAX_KGDB_PORT2)
+ cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2
+ beq sigint
+ nop
+#elif defined(CONFIG_ETRAX_KGDB_PORT3)
+ cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3
+ beq sigint
+ nop
+#endif
+;; Multiple interrupt must be due to serial break.
+ cmp.d 0x30, $r10 ; Multiple interrupt
+ beq sigint
+ nop
+;; Neither of those? Then it's a sigtrap.
+ ba handle_comm
+ moveq 5, $r10 ; Set SIGTRAP (delay slot)
+
+sigint:
+ ;; Serial interrupt; get character
+ jsr getDebugChar
+ nop ; Delay slot
+ cmp.b 3, $r10 ; \003 (Ctrl-C)?
+ bne return ; No, get out of here
+ nop
+ moveq 2, $r10 ; Set SIGINT
+
+;;
+;; Handle the communication
+;;
+handle_comm:
+ move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards
+ jsr handle_exception ; Interactive routine
+ nop
+
+;;
+;; Return to the caller
+;;
+return:
+
+;; First of all, write the support registers.
+ clear.d $r1 ; Bank counter
+ move.d sreg, $acr
+
+;; Bank 0
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move.d [$acr], $r0
+ move $r0, $s0
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s1
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s2
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s3
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s4
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s5
+ addq 4, $acr
+
+;; Nothing in S6 - S7, bank 0.
+ addq 4, $acr
+ addq 4, $acr
+
+ move.d [$acr], $r0
+ move $r0, $s8
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s9
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s10
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s11
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s12
+ addq 4, $acr
+
+;; Nothing in S13 - S15, bank 0
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+
+;; Bank 1 and bank 2 have the same layout, hence the loop.
+ addq 1, $r1
+2:
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move.d [$acr], $r0
+ move $r0, $s0
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s1
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s2
+ addq 4, $acr
+
+;; S3 (MM_CAUSE) is read-only.
+ addq 4, $acr
+
+ move.d [$acr], $r0
+ move $r0, $s4
+ addq 4, $acr
+
+;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.)
+ addq 4, $acr
+ addq 4, $acr
+
+;; Nothing in S7 - S15, bank 1 and 2
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+ addq 4, $acr
+
+ addq 1, $r1
+ cmpq 3, $r1
+ bne 2b
+ nop
+
+;; Bank 3
+ move $r1, $srs
+ nop
+ nop
+ nop
+ move.d [$acr], $r0
+ move $r0, $s0
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s1
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s2
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s3
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s4
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s5
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s6
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s7
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s8
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s9
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s10
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s11
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s12
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s13
+ addq 4, $acr
+ move.d [$acr], $r0
+ move $r0, $s14
+ addq 4, $acr
+
+;; Nothing in S15, bank 3
+ addq 4, $acr
+
+;; Now, move on to the regular register restoration process.
+
+ move.d reg, $acr ; Reset ACR to point at the beginning of the register image
+ move.d [$acr], $r0 ; Restore R0
+ addq 4, $acr
+ move.d [$acr], $r1 ; Restore R1
+ addq 4, $acr
+ move.d [$acr], $r2 ; Restore R2
+ addq 4, $acr
+ move.d [$acr], $r3 ; Restore R3
+ addq 4, $acr
+ move.d [$acr], $r4 ; Restore R4
+ addq 4, $acr
+ move.d [$acr], $r5 ; Restore R5
+ addq 4, $acr
+ move.d [$acr], $r6 ; Restore R6
+ addq 4, $acr
+ move.d [$acr], $r7 ; Restore R7
+ addq 4, $acr
+ move.d [$acr], $r8 ; Restore R8
+ addq 4, $acr
+ move.d [$acr], $r9 ; Restore R9
+ addq 4, $acr
+ move.d [$acr], $r10 ; Restore R10
+ addq 4, $acr
+ move.d [$acr], $r11 ; Restore R11
+ addq 4, $acr
+ move.d [$acr], $r12 ; Restore R12
+ addq 4, $acr
+ move.d [$acr], $r13 ; Restore R13
+
+;;
+;; We restore all registers, even though some of them probably haven't changed.
+;;
+
+ addq 4, $acr
+ move.d [$acr], $sp ; Restore SP (R14)
+
+ ;; ACR cannot be restored just yet.
+ addq 8, $acr
+
+ ;; Skip BZ, VR.
+ addq 2, $acr
+
+ move [$acr], $pid ; Restore PID
+ addq 4, $acr
+ move [$acr], $srs ; Restore SRS
+ nop
+ nop
+ nop
+ addq 1, $acr
+
+ ;; Skip WZ.
+ addq 2, $acr
+
+ move [$acr], $exs ; Restore EXS.
+ addq 4, $acr
+ move [$acr], $eda ; Restore EDA.
+ addq 4, $acr
+ move [$acr], $mof ; Restore MOF.
+
+ ;; Skip DZ.
+ addq 8, $acr
+
+ move [$acr], $ebp ; Restore EBP.
+ addq 4, $acr
+ move [$acr], $erp ; Restore ERP.
+ addq 4, $acr
+ move [$acr], $srp ; Restore SRP.
+ addq 4, $acr
+ move [$acr], $nrp ; Restore NRP.
+ addq 4, $acr
+ move [$acr], $ccs ; Restore CCS like an ordinary register.
+ addq 4, $acr
+ move [$acr], $usp ; Restore USP
+ addq 4, $acr
+ move [$acr], $spc ; Restore SPC
+ ; No restoration of pseudo-PC of course.
+
+ move.d reg, $acr ; Reset ACR to point at the beginning of the register image
+ add.d 15*4, $acr
+ move.d [$acr], $acr ; Finally, restore ACR.
+ rete ; Same as jump ERP
+ rfe ; Shifts CCS
diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c
new file mode 100644
index 000000000..cebd32e2a
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/process.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2000-2003 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ * Mikael Starvik (starvik@axis.com)
+ * Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
+ *
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/timer_defs.h>
+#include <hwregs/intr_vect_defs.h>
+#include <linux/ptrace.h>
+
+extern void stop_watchdog(void);
+
+/* We use this if we don't have any better idle routine. */
+void default_idle(void)
+{
+ /* Halt until exception. */
+ __asm__ volatile("ei \n\t"
+ "halt ");
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+
+extern void deconfigure_bp(long pid);
+void exit_thread(void)
+{
+ deconfigure_bp(current->pid);
+}
+
+/*
+ * If the watchdog is enabled, disable interrupts and enter an infinite loop.
+ * The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled
+ * then enable it and wait.
+ */
+extern void arch_enable_nmi(void);
+
+void
+hard_reset_now(void)
+{
+ /*
+ * Don't declare this variable elsewhere. We don't want any other
+ * code to know about it than the watchdog handler in entry.S and
+ * this code, implementing hard reset through the watchdog.
+ */
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ extern int cause_of_death;
+#endif
+
+ printk("*** HARD RESET ***\n");
+ local_irq_disable();
+
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ cause_of_death = 0xbedead;
+#else
+{
+ reg_timer_rw_wd_ctrl wd_ctrl = {0};
+
+ stop_watchdog();
+
+ wd_ctrl.key = 16; /* Arbitrary key. */
+ wd_ctrl.cnt = 1; /* Minimum time. */
+ wd_ctrl.cmd = regk_timer_start;
+
+ arch_enable_nmi();
+ REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
+}
+#endif
+
+ while (1)
+ ; /* Wait for reset. */
+}
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+unsigned long thread_saved_pc(struct task_struct *t)
+{
+ return task_pt_regs(t)->erp;
+}
+
+/*
+ * Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
+ * It will be unnested during _resume and _ret_from_sys_call when the new thread
+ * is scheduled.
+ *
+ * Also setup the thread switching structure which is used to keep
+ * thread-specific data during _resumes.
+ */
+
+extern asmlinkage void ret_from_fork(void);
+extern asmlinkage void ret_from_kernel_thread(void);
+
+int
+copy_thread(unsigned long clone_flags, unsigned long usp,
+ unsigned long arg, struct task_struct *p)
+{
+ struct pt_regs *childregs = task_pt_regs(p);
+ struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
+
+ /*
+ * Put the pt_regs structure at the end of the new kernel stack page and
+ * fix it up. Note: the task_struct doubles as the kernel stack for the
+ * task.
+ */
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(swstack, 0,
+ sizeof(struct switch_stack) + sizeof(struct pt_regs));
+ swstack->r1 = usp;
+ swstack->r2 = arg;
+ childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
+ swstack->return_ip = (unsigned long) ret_from_kernel_thread;
+ p->thread.ksp = (unsigned long) swstack;
+ p->thread.usp = 0;
+ return 0;
+ }
+ *childregs = *current_pt_regs(); /* Struct copy of pt_regs. */
+ childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
+
+ /* Set a new TLS ?
+ * The TLS is in $mof because it is the 5th argument to sys_clone.
+ */
+ if (p->mm && (clone_flags & CLONE_SETTLS)) {
+ task_thread_info(p)->tls = childregs->mof;
+ }
+
+ /* Put the switch stack right below the pt_regs. */
+
+ /* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */
+ swstack->r9 = 0;
+
+ /*
+ * We want to return into ret_from_sys_call after the _resume.
+ * ret_from_fork will call ret_from_sys_call.
+ */
+ swstack->return_ip = (unsigned long) ret_from_fork;
+
+ /* Fix the user-mode and kernel-mode stackpointer. */
+ p->thread.usp = usp ?: rdusp();
+ p->thread.ksp = (unsigned long) swstack;
+
+ return 0;
+}
+
+unsigned long
+get_wchan(struct task_struct *p)
+{
+ /* TODO */
+ return 0;
+}
+#undef last_sched
+#undef first_sched
+
+void show_regs(struct pt_regs * regs)
+{
+ unsigned long usp = rdusp();
+
+ show_regs_print_info(KERN_DEFAULT);
+
+ printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
+ regs->erp, regs->srp, regs->ccs, usp, regs->mof);
+
+ printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+
+ printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+
+ printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
+ regs->r8, regs->r9, regs->r10, regs->r11);
+
+ printk("r12: %08lx r13: %08lx oR10: %08lx\n",
+ regs->r12, regs->r13, regs->orig_r10);
+}
diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c
new file mode 100644
index 000000000..f085229cf
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/ptrace.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2000-2007, Axis Communications AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/signal.h>
+#include <linux/security.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <arch/hwregs/supp_reg.h>
+
+/*
+ * Determines which bits in CCS the user has access to.
+ * 1 = access, 0 = no access.
+ */
+#define CCS_MASK 0x00087c00 /* SXNZVC */
+
+#define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT))
+
+static int put_debugreg(long pid, unsigned int regno, long data);
+static long get_debugreg(long pid, unsigned int regno);
+static unsigned long get_pseudo_pc(struct task_struct *child);
+void deconfigure_bp(long pid);
+
+extern unsigned long cris_signal_return_page;
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+long get_reg(struct task_struct *task, unsigned int regno)
+{
+ /* USP is a special case, it's not in the pt_regs struct but
+ * in the tasks thread struct
+ */
+ unsigned long ret;
+
+ if (regno <= PT_EDA)
+ ret = ((unsigned long *)task_pt_regs(task))[regno];
+ else if (regno == PT_USP)
+ ret = task->thread.usp;
+ else if (regno == PT_PPC)
+ ret = get_pseudo_pc(task);
+ else if (regno <= PT_MAX)
+ ret = get_debugreg(task->pid, regno);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+int put_reg(struct task_struct *task, unsigned int regno, unsigned long data)
+{
+ if (regno <= PT_EDA)
+ ((unsigned long *)task_pt_regs(task))[regno] = data;
+ else if (regno == PT_USP)
+ task->thread.usp = data;
+ else if (regno == PT_PPC) {
+ /* Write pseudo-PC to ERP only if changed. */
+ if (data != get_pseudo_pc(task))
+ task_pt_regs(task)->erp = data;
+ } else if (regno <= PT_MAX)
+ return put_debugreg(task->pid, regno, data);
+ else
+ return -1;
+ return 0;
+}
+
+void user_enable_single_step(struct task_struct *child)
+{
+ unsigned long tmp;
+
+ /*
+ * Set up SPC if not set already (in which case we have no other
+ * choice but to trust it).
+ */
+ if (!get_reg(child, PT_SPC)) {
+ /* In case we're stopped in a delay slot. */
+ tmp = get_reg(child, PT_ERP) & ~1;
+ put_reg(child, PT_SPC, tmp);
+ }
+ tmp = get_reg(child, PT_CCS) | SBIT_USER;
+ put_reg(child, PT_CCS, tmp);
+}
+
+void user_disable_single_step(struct task_struct *child)
+{
+ put_reg(child, PT_SPC, 0);
+
+ if (!get_debugreg(child->pid, PT_BP_CTRL)) {
+ unsigned long tmp;
+ /* If no h/w bp configured, disable S bit. */
+ tmp = get_reg(child, PT_CCS) & ~SBIT_USER;
+ put_reg(child, PT_CCS, tmp);
+ }
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching.
+ *
+ * Make sure the single step bit is not set.
+ */
+void
+ptrace_disable(struct task_struct *child)
+{
+ /* Deconfigure SPC and S-bit. */
+ user_disable_single_step(child);
+ put_reg(child, PT_SPC, 0);
+
+ /* Deconfigure any watchpoints associated with the child. */
+ deconfigure_bp(child->pid);
+}
+
+
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ int ret;
+ unsigned int regno = addr >> 2;
+ unsigned long __user *datap = (unsigned long __user *)data;
+
+ switch (request) {
+ /* Read word at location address. */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ ret = -EIO;
+
+ /* The signal trampoline page is outside the normal user-addressable
+ * space but still accessible. This is hack to make it possible to
+ * access the signal handler code in GDB.
+ */
+ if ((addr & PAGE_MASK) == cris_signal_return_page) {
+ /* The trampoline page is globally mapped, no page table to traverse.*/
+ tmp = *(unsigned long*)addr;
+ } else {
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+
+ if (copied != sizeof(tmp))
+ break;
+ }
+
+ ret = put_user(tmp,datap);
+ break;
+ }
+
+ /* Read the word at location address in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || regno > PT_MAX)
+ break;
+
+ tmp = get_reg(child, regno);
+ ret = put_user(tmp, datap);
+ break;
+ }
+
+ /* Write the word at location address. */
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = generic_ptrace_pokedata(child, addr, data);
+ break;
+
+ /* Write the word at location address in the USER area. */
+ case PTRACE_POKEUSR:
+ ret = -EIO;
+ if ((addr & 3) || regno > PT_MAX)
+ break;
+
+ if (regno == PT_CCS) {
+ /* don't allow the tracing process to change stuff like
+ * interrupt enable, kernel/user bit, dma enables etc.
+ */
+ data &= CCS_MASK;
+ data |= get_reg(child, PT_CCS) & ~CCS_MASK;
+ }
+ if (put_reg(child, regno, data))
+ break;
+ ret = 0;
+ break;
+
+ /* Get all GP registers from the child. */
+ case PTRACE_GETREGS: {
+ int i;
+ unsigned long tmp;
+
+ for (i = 0; i <= PT_MAX; i++) {
+ tmp = get_reg(child, i);
+
+ if (put_user(tmp, datap)) {
+ ret = -EFAULT;
+ goto out_tsk;
+ }
+
+ datap++;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ /* Set all GP registers in the child. */
+ case PTRACE_SETREGS: {
+ int i;
+ unsigned long tmp;
+
+ for (i = 0; i <= PT_MAX; i++) {
+ if (get_user(tmp, datap)) {
+ ret = -EFAULT;
+ goto out_tsk;
+ }
+
+ if (i == PT_CCS) {
+ tmp &= CCS_MASK;
+ tmp |= get_reg(child, PT_CCS) & ~CCS_MASK;
+ }
+
+ put_reg(child, i, tmp);
+ datap++;
+ }
+
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+out_tsk:
+ return ret;
+}
+
+void do_syscall_trace(void)
+{
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+
+ if (!(current->ptrace & PT_PTRACED))
+ return;
+
+ /* the 0x80 provides a way for the tracing parent to distinguish
+ between a syscall stop and SIGTRAP delivery */
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
+
+ /*
+ * This isn't the same as continuing with a signal, but it will do for
+ * normal use.
+ */
+ if (current->exit_code) {
+ send_sig(current->exit_code, current, 1);
+ current->exit_code = 0;
+ }
+}
+
+/* Returns the size of an instruction that has a delay slot. */
+
+static int insn_size(struct task_struct *child, unsigned long pc)
+{
+ unsigned long opcode;
+ int copied;
+ int opsize = 0;
+
+ /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
+ copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
+ if (copied != sizeof(opcode))
+ return 0;
+
+ switch ((opcode & 0x0f00) >> 8) {
+ case 0x0:
+ case 0x9:
+ case 0xb:
+ opsize = 2;
+ break;
+ case 0xe:
+ case 0xf:
+ opsize = 6;
+ break;
+ case 0xd:
+ /* Could be 4 or 6; check more bits. */
+ if ((opcode & 0xff) == 0xff)
+ opsize = 4;
+ else
+ opsize = 6;
+ break;
+ default:
+ panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n",
+ opcode, pc);
+ }
+
+ return opsize;
+}
+
+static unsigned long get_pseudo_pc(struct task_struct *child)
+{
+ /* Default value for PC is ERP. */
+ unsigned long pc = get_reg(child, PT_ERP);
+
+ if (pc & 0x1) {
+ unsigned long spc = get_reg(child, PT_SPC);
+ /* Delay slot bit set. Report as stopped on proper
+ instruction. */
+ if (spc) {
+ /* Rely on SPC if set. FIXME: We might want to check
+ that EXS indicates we stopped due to a single-step
+ exception. */
+ pc = spc;
+ } else {
+ /* Calculate the PC from the size of the instruction
+ that the delay slot we're in belongs to. */
+ pc += insn_size(child, pc & ~1) - 1;
+ }
+ }
+ return pc;
+}
+
+static long bp_owner = 0;
+
+/* Reachable from exit_thread in signal.c, so not static. */
+void deconfigure_bp(long pid)
+{
+ int bp;
+
+ /* Only deconfigure if the pid is the owner. */
+ if (bp_owner != pid)
+ return;
+
+ for (bp = 0; bp < 6; bp++) {
+ unsigned long tmp;
+ /* Deconfigure start and end address (also gets rid of ownership). */
+ put_debugreg(pid, PT_BP + 3 + (bp * 2), 0);
+ put_debugreg(pid, PT_BP + 4 + (bp * 2), 0);
+
+ /* Deconfigure relevant bits in control register. */
+ tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4)));
+ put_debugreg(pid, PT_BP_CTRL, tmp);
+ }
+ /* No owner now. */
+ bp_owner = 0;
+}
+
+static int put_debugreg(long pid, unsigned int regno, long data)
+{
+ int ret = 0;
+ register int old_srs;
+
+#ifdef CONFIG_ETRAX_KGDB
+ /* Ignore write, but pretend it was ok if value is 0
+ (we don't want POKEUSR/SETREGS failing unnessecarily). */
+ return (data == 0) ? ret : -1;
+#endif
+
+ /* Simple owner management. */
+ if (!bp_owner)
+ bp_owner = pid;
+ else if (bp_owner != pid) {
+ /* Ignore write, but pretend it was ok if value is 0
+ (we don't want POKEUSR/SETREGS failing unnessecarily). */
+ return (data == 0) ? ret : -1;
+ }
+
+ /* Remember old SRS. */
+ SPEC_REG_RD(SPEC_REG_SRS, old_srs);
+ /* Switch to BP bank. */
+ SUPP_BANK_SEL(BANK_BP);
+
+ switch (regno - PT_BP) {
+ case 0:
+ SUPP_REG_WR(0, data); break;
+ case 1:
+ case 2:
+ if (data)
+ ret = -1;
+ break;
+ case 3:
+ SUPP_REG_WR(3, data); break;
+ case 4:
+ SUPP_REG_WR(4, data); break;
+ case 5:
+ SUPP_REG_WR(5, data); break;
+ case 6:
+ SUPP_REG_WR(6, data); break;
+ case 7:
+ SUPP_REG_WR(7, data); break;
+ case 8:
+ SUPP_REG_WR(8, data); break;
+ case 9:
+ SUPP_REG_WR(9, data); break;
+ case 10:
+ SUPP_REG_WR(10, data); break;
+ case 11:
+ SUPP_REG_WR(11, data); break;
+ case 12:
+ SUPP_REG_WR(12, data); break;
+ case 13:
+ SUPP_REG_WR(13, data); break;
+ case 14:
+ SUPP_REG_WR(14, data); break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ /* Restore SRS. */
+ SPEC_REG_WR(SPEC_REG_SRS, old_srs);
+ /* Just for show. */
+ NOP();
+ NOP();
+ NOP();
+
+ return ret;
+}
+
+static long get_debugreg(long pid, unsigned int regno)
+{
+ register int old_srs;
+ register long data;
+
+ if (pid != bp_owner) {
+ return 0;
+ }
+
+ /* Remember old SRS. */
+ SPEC_REG_RD(SPEC_REG_SRS, old_srs);
+ /* Switch to BP bank. */
+ SUPP_BANK_SEL(BANK_BP);
+
+ switch (regno - PT_BP) {
+ case 0:
+ SUPP_REG_RD(0, data); break;
+ case 1:
+ case 2:
+ /* error return value? */
+ data = 0;
+ break;
+ case 3:
+ SUPP_REG_RD(3, data); break;
+ case 4:
+ SUPP_REG_RD(4, data); break;
+ case 5:
+ SUPP_REG_RD(5, data); break;
+ case 6:
+ SUPP_REG_RD(6, data); break;
+ case 7:
+ SUPP_REG_RD(7, data); break;
+ case 8:
+ SUPP_REG_RD(8, data); break;
+ case 9:
+ SUPP_REG_RD(9, data); break;
+ case 10:
+ SUPP_REG_RD(10, data); break;
+ case 11:
+ SUPP_REG_RD(11, data); break;
+ case 12:
+ SUPP_REG_RD(12, data); break;
+ case 13:
+ SUPP_REG_RD(13, data); break;
+ case 14:
+ SUPP_REG_RD(14, data); break;
+ default:
+ /* error return value? */
+ data = 0;
+ }
+
+ /* Restore SRS. */
+ SPEC_REG_WR(SPEC_REG_SRS, old_srs);
+ /* Just for show. */
+ NOP();
+ NOP();
+ NOP();
+
+ return data;
+}
diff --git a/arch/cris/arch-v32/kernel/setup.c b/arch/cris/arch-v32/kernel/setup.c
new file mode 100644
index 000000000..cd1865d68
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/setup.c
@@ -0,0 +1,170 @@
+/*
+ * Display CPU info in /proc/cpuinfo.
+ *
+ * Copyright (C) 2003, Axis Communications AB.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/param.h>
+
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_PROC_FS
+
+#define HAS_FPU 0x0001
+#define HAS_MMU 0x0002
+#define HAS_ETHERNET100 0x0004
+#define HAS_TOKENRING 0x0008
+#define HAS_SCSI 0x0010
+#define HAS_ATA 0x0020
+#define HAS_USB 0x0040
+#define HAS_IRQ_BUG 0x0080
+#define HAS_MMU_BUG 0x0100
+
+struct cpu_info {
+ char *cpu_model;
+ unsigned short rev;
+ unsigned short cache_size;
+ unsigned short flags;
+};
+
+/* Some of these model are here for historical reasons only. */
+static struct cpu_info cpinfo[] = {
+ {"ETRAX 1", 0, 0, 0},
+ {"ETRAX 2", 1, 0, 0},
+ {"ETRAX 3", 2, 0, 0},
+ {"ETRAX 4", 3, 0, 0},
+ {"Simulator", 7, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
+ {"ETRAX 100", 8, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG},
+ {"ETRAX 100", 9, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
+
+ {"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
+ | HAS_MMU | HAS_MMU_BUG},
+
+ {"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
+ | HAS_MMU},
+#ifdef CONFIG_ETRAXFS
+ {"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
+#else
+ {"ARTPEC-3", 32, 32, HAS_ETHERNET100 | HAS_MMU},
+#endif
+ {"Unknown", 0, 0, 0}
+};
+
+int show_cpuinfo(struct seq_file *m, void *v)
+{
+ int i;
+ int cpu = (int)v - 1;
+ unsigned long revision;
+ struct cpu_info *info;
+
+ info = &cpinfo[ARRAY_SIZE(cpinfo) - 1];
+
+ revision = rdvr();
+
+ for (i = 0; i < ARRAY_SIZE(cpinfo); i++) {
+ if (cpinfo[i].rev == revision) {
+ info = &cpinfo[i];
+ break;
+ }
+ }
+
+ seq_printf(m,
+ "processor\t: %d\n"
+ "cpu\t\t: CRIS\n"
+ "cpu revision\t: %lu\n"
+ "cpu model\t: %s\n"
+ "cache size\t: %d KB\n"
+ "fpu\t\t: %s\n"
+ "mmu\t\t: %s\n"
+ "mmu DMA bug\t: %s\n"
+ "ethernet\t: %s Mbps\n"
+ "token ring\t: %s\n"
+ "scsi\t\t: %s\n"
+ "ata\t\t: %s\n"
+ "usb\t\t: %s\n"
+ "bogomips\t: %lu.%02lu\n\n",
+
+ cpu,
+ revision,
+ info->cpu_model,
+ info->cache_size,
+ info->flags & HAS_FPU ? "yes" : "no",
+ info->flags & HAS_MMU ? "yes" : "no",
+ info->flags & HAS_MMU_BUG ? "yes" : "no",
+ info->flags & HAS_ETHERNET100 ? "10/100" : "10",
+ info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
+ info->flags & HAS_SCSI ? "yes" : "no",
+ info->flags & HAS_ATA ? "yes" : "no",
+ info->flags & HAS_USB ? "yes" : "no",
+ (loops_per_jiffy * HZ + 500) / 500000,
+ ((loops_per_jiffy * HZ + 500) / 5000) % 100);
+
+ return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+void show_etrax_copyright(void)
+{
+#ifdef CONFIG_ETRAXFS
+ printk(KERN_INFO "Linux/CRISv32 port on ETRAX FS "
+ "(C) 2003, 2004 Axis Communications AB\n");
+#else
+ printk(KERN_INFO "Linux/CRISv32 port on ARTPEC-3 "
+ "(C) 2003-2009 Axis Communications AB\n");
+#endif
+}
+
+static struct i2c_board_info __initdata i2c_info[] = {
+ {I2C_BOARD_INFO("camblock", 0x43)},
+ {I2C_BOARD_INFO("tmp100", 0x48)},
+ {I2C_BOARD_INFO("tmp100", 0x4A)},
+ {I2C_BOARD_INFO("tmp100", 0x4C)},
+ {I2C_BOARD_INFO("tmp100", 0x4D)},
+ {I2C_BOARD_INFO("tmp100", 0x4E)},
+#ifdef CONFIG_RTC_DRV_PCF8563
+ {I2C_BOARD_INFO("pcf8563", 0x51)},
+#endif
+#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
+ {I2C_BOARD_INFO("vgpio", 0x20)},
+ {I2C_BOARD_INFO("vgpio", 0x21)},
+#endif
+ {I2C_BOARD_INFO("pca9536", 0x41)},
+ {I2C_BOARD_INFO("fnp300", 0x40)},
+ {I2C_BOARD_INFO("fnp300", 0x42)},
+ {I2C_BOARD_INFO("adc101", 0x54)},
+};
+
+static struct i2c_board_info __initdata i2c_info2[] = {
+ {I2C_BOARD_INFO("camblock", 0x43)},
+ {I2C_BOARD_INFO("tmp100", 0x48)},
+ {I2C_BOARD_INFO("tmp100", 0x4A)},
+ {I2C_BOARD_INFO("tmp100", 0x4C)},
+ {I2C_BOARD_INFO("tmp100", 0x4D)},
+ {I2C_BOARD_INFO("tmp100", 0x4E)},
+#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
+ {I2C_BOARD_INFO("vgpio", 0x20)},
+ {I2C_BOARD_INFO("vgpio", 0x21)},
+#endif
+ {I2C_BOARD_INFO("pca9536", 0x41)},
+ {I2C_BOARD_INFO("fnp300", 0x40)},
+ {I2C_BOARD_INFO("fnp300", 0x42)},
+ {I2C_BOARD_INFO("adc101", 0x54)},
+};
+
+static struct i2c_board_info __initdata i2c_info3[] = {
+ {I2C_BOARD_INFO("adc101", 0x54)},
+};
+
+static int __init etrax_init(void)
+{
+ i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
+ i2c_register_board_info(1, i2c_info2, ARRAY_SIZE(i2c_info2));
+ i2c_register_board_info(2, i2c_info3, ARRAY_SIZE(i2c_info3));
+ return 0;
+}
+arch_initcall(etrax_init);
diff --git a/arch/cris/arch-v32/kernel/signal.c b/arch/cris/arch-v32/kernel/signal.c
new file mode 100644
index 000000000..3a36ae6b7
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/signal.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2003, Axis Communications AB.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/syscalls.h>
+#include <linux/vmalloc.h>
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <arch/ptrace.h>
+#include <arch/hwregs/cpu_vect.h>
+
+extern unsigned long cris_signal_return_page;
+
+/*
+ * A syscall in CRIS is really a "break 13" instruction, which is 2
+ * bytes. The registers is manipulated so upon return the instruction
+ * will be executed again.
+ *
+ * This relies on that PC points to the instruction after the break call.
+ */
+#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2;
+
+/* Signal frames. */
+struct signal_frame {
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS - 1];
+ unsigned char retcode[8]; /* Trampoline code. */
+};
+
+struct rt_signal_frame {
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ unsigned char retcode[8]; /* Trampoline code. */
+};
+
+void do_signal(int restart, struct pt_regs *regs);
+void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
+ struct pt_regs *regs);
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+ unsigned int err = 0;
+ unsigned long old_usp;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Restore the registers from &sc->regs. sc is already checked
+ * for VERIFY_READ since the signal_frame was previously
+ * checked in sys_sigreturn().
+ */
+ if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
+ goto badframe;
+
+ /* Make that the user-mode flag is set. */
+ regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT));
+
+ /* Don't perform syscall restarting */
+ regs->exs = -1;
+
+ /* Restore the old USP. */
+ err |= __get_user(old_usp, &sc->usp);
+ wrusp(old_usp);
+
+ return err;
+
+badframe:
+ return 1;
+}
+
+asmlinkage int sys_sigreturn(void)
+{
+ struct pt_regs *regs = current_pt_regs();
+ sigset_t set;
+ struct signal_frame __user *frame;
+ unsigned long oldspc = regs->spc;
+ unsigned long oldccs = regs->ccs;
+
+ frame = (struct signal_frame *) rdusp();
+
+ /*
+ * Since the signal is stacked on a dword boundary, the frame
+ * should be dword aligned here as well. It it's not, then the
+ * user is trying some funny business.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask) ||
+ (_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1],
+ frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(regs, &frame->sc))
+ goto badframe;
+
+ keep_debug_flags(oldccs, oldspc, regs);
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(void)
+{
+ struct pt_regs *regs = current_pt_regs();
+ sigset_t set;
+ struct rt_signal_frame __user *frame;
+ unsigned long oldspc = regs->spc;
+ unsigned long oldccs = regs->ccs;
+
+ frame = (struct rt_signal_frame *) rdusp();
+
+ /*
+ * Since the signal is stacked on a dword boundary, the frame
+ * should be dword aligned here as well. It it's not, then the
+ * user is trying some funny business.
+ */
+ if (((long)frame) & 3)
+ goto badframe;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ if (restore_altstack(&frame->uc.uc_stack))
+ goto badframe;
+
+ keep_debug_flags(oldccs, oldspc, regs);
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/* Setup a signal frame. */
+static int
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
+ unsigned long mask)
+{
+ int err;
+ unsigned long usp;
+
+ err = 0;
+ usp = rdusp();
+
+ /*
+ * Copy the registers. They are located first in sc, so it's
+ * possible to use sc directly.
+ */
+ err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
+
+ err |= __put_user(mask, &sc->oldmask);
+ err |= __put_user(usp, &sc->usp);
+
+ return err;
+}
+
+/* Figure out where to put the new signal frame - usually on the stack. */
+static inline void __user *
+get_sigframe(struct ksignal *ksig, size_t frame_size)
+{
+ unsigned long sp = sigsp(rdusp(), ksig);
+
+ /* Make sure the frame is dword-aligned. */
+ sp &= ~3;
+
+ return (void __user *)(sp - 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 trampiline
+ * which performs the syscall sigreturn(), or a provided user-mode
+ * trampoline.
+ */
+static int
+setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
+{
+ int err;
+ unsigned long return_ip;
+ struct signal_frame __user *frame;
+
+ err = 0;
+ frame = get_sigframe(ksig, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+
+ if (err)
+ return -EFAULT;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+
+ if (err)
+ return -EFAULT;
+
+ /*
+ * Set up to return from user-space. If provided, use a stub
+ * already located in user-space.
+ */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ksig->ka.sa.sa_restorer;
+ } else {
+ /* Trampoline - the desired return ip is in the signal return page. */
+ return_ip = cris_signal_return_page;
+
+ /*
+ * This is movu.w __NR_sigreturn, r9; break 13;
+ *
+ * WE DO NOT USE IT ANY MORE! It's only left here for historical
+ * reasons and because gdb uses it as a signature to notice
+ * signal handler stack frames.
+ */
+ err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
+ err |= __put_user(__NR_sigreturn, (short __user*)(frame->retcode+2));
+ err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
+ }
+
+ if (err)
+ return -EFAULT;
+
+ /*
+ * Set up registers for signal handler.
+ *
+ * Where the code enters now.
+ * Where the code enter later.
+ * First argument, signo.
+ */
+ regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
+ regs->srp = return_ip;
+ regs->r10 = ksig->sig;
+
+ /* Actually move the USP to reflect the stacked frame. */
+ wrusp((unsigned long)frame);
+
+ return 0;
+}
+
+static int
+setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
+{
+ int err;
+ unsigned long return_ip;
+ struct rt_signal_frame __user *frame;
+
+ err = 0;
+ frame = get_sigframe(ksig, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, &ksig->info);
+
+ if (err)
+ return -EFAULT;
+
+ /* Clear all the bits of the ucontext we don't use. */
+ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+ err |= __save_altstack(&frame->uc.uc_stack, rdusp());
+
+ if (err)
+ return -EFAULT;
+
+ /*
+ * Set up to return from user-space. If provided, use a stub
+ * already located in user-space.
+ */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long) ksig->ka.sa.sa_restorer;
+ } else {
+ /* Trampoline - the desired return ip is in the signal return page. */
+ return_ip = cris_signal_return_page + 6;
+
+ /*
+ * This is movu.w __NR_rt_sigreturn, r9; break 13;
+ *
+ * WE DO NOT USE IT ANY MORE! It's only left here for historical
+ * reasons and because gdb uses it as a signature to notice
+ * signal handler stack frames.
+ */
+ err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
+
+ err |= __put_user(__NR_rt_sigreturn,
+ (short __user*)(frame->retcode+2));
+
+ err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
+ }
+
+ if (err)
+ return -EFAULT;
+
+ /*
+ * Set up registers for signal handler.
+ *
+ * Where the code enters now.
+ * Where the code enters later.
+ * First argument is signo.
+ * Second argument is (siginfo_t *).
+ * Third argument is unused.
+ */
+ regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
+ regs->srp = return_ip;
+ regs->r10 = ksig->sig;
+ regs->r11 = (unsigned long) &frame->info;
+ regs->r12 = 0;
+
+ /* Actually move the usp to reflect the stacked frame. */
+ wrusp((unsigned long)frame);
+
+ return 0;
+}
+
+/* Invoke a signal handler to, well, handle the signal. */
+static inline void
+handle_signal(int canrestart, struct ksignal *ksig, struct pt_regs *regs)
+{
+ sigset_t *oldset = sigmask_to_save();
+ int ret;
+
+ /* Check if this got called from a system call. */
+ if (canrestart) {
+ /* If so, check system call restarting. */
+ switch (regs->r10) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ /*
+ * This means that the syscall should
+ * only be restarted if there was no
+ * handler for the signal, and since
+ * this point isn't reached unless
+ * there is a handler, there's no need
+ * to restart.
+ */
+ regs->r10 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ /*
+ * This means restart the syscall if
+ * there is no handler, or the handler
+ * was registered with SA_RESTART.
+ */
+ if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
+ regs->r10 = -EINTR;
+ break;
+ }
+
+ /* Fall through. */
+
+ case -ERESTARTNOINTR:
+ /*
+ * This means that the syscall should
+ * be called again after the signal
+ * handler returns.
+ */
+ RESTART_CRIS_SYS(regs);
+ break;
+ }
+ }
+
+ /* Set up the stack frame. */
+ if (ksig->ka.sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame(ksig, oldset, regs);
+ else
+ ret = setup_frame(ksig, oldset, regs);
+
+ signal_setup_done(ret, ksig, 0);
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Also note that the regs structure given here as an argument, is the latest
+ * pushed pt_regs. It may or may not be the same as the first pushed registers
+ * when the initial usermode->kernelmode transition took place. Therefore
+ * we can use user_mode(regs) to see if we came directly from kernel or user
+ * mode below.
+ */
+void
+do_signal(int canrestart, struct pt_regs *regs)
+{
+ struct ksignal ksig;
+
+ canrestart = canrestart && ((int)regs->exs >= 0);
+
+ /*
+ * The common case should go fast, which is why this point is
+ * reached from kernel-mode. If that's the case, just return
+ * without doing anything.
+ */
+ if (!user_mode(regs))
+ return;
+
+ if (get_signal(&ksig)) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(canrestart, &ksig, regs);
+ return;
+ }
+
+ /* Got here from a system call? */
+ if (canrestart) {
+ /* Restart the system call - no handlers present. */
+ if (regs->r10 == -ERESTARTNOHAND ||
+ regs->r10 == -ERESTARTSYS ||
+ regs->r10 == -ERESTARTNOINTR) {
+ RESTART_CRIS_SYS(regs);
+ }
+
+ if (regs->r10 == -ERESTART_RESTARTBLOCK){
+ regs->r9 = __NR_restart_syscall;
+ regs->erp -= 2;
+ }
+ }
+
+ /* if there's no signal to deliver, we just put the saved sigmask
+ * back */
+ restore_saved_sigmask();
+}
+
+asmlinkage void
+ugdb_trap_user(struct thread_info *ti, int sig)
+{
+ if (((user_regs(ti)->exs & 0xff00) >> 8) != SINGLE_STEP_INTR_VECT) {
+ /* Zero single-step PC if the reason we stopped wasn't a single
+ step exception. This is to avoid relying on it when it isn't
+ reliable. */
+ 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
+ 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. */
+ if (!(user_regs(ti)->erp & 0x1))
+ user_regs(ti)->erp -= 2;
+ }
+ sys_kill(ti->task->pid, sig);
+}
+
+void
+keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
+ struct pt_regs *regs)
+{
+ if (oldccs & (1 << Q_CCS_BITNR)) {
+ /* Pending single step due to single-stepping the break 13
+ in the signal trampoline: keep the Q flag. */
+ regs->ccs |= (1 << Q_CCS_BITNR);
+ /* S flag should be set - complain if it's not. */
+ if (!(oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT)))) {
+ printk("Q flag but no S flag?");
+ }
+ regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+ /* Assume the SPC is valid and interesting. */
+ regs->spc = oldspc;
+
+ } else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) {
+ /* If a h/w bp was set in the signal handler we need
+ to keep the S flag. */
+ regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+ /* Don't keep the old SPC though; if we got here due to
+ a single-step, the Q flag should have been set. */
+ } else if (regs->spc) {
+ /* If we were single-stepping *before* the signal was taken,
+ we don't want to restore that state now, because GDB will
+ have forgotten all about it. */
+ regs->spc = 0;
+ regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
+ }
+}
+
+/* Set up the trampolines on the signal return page. */
+int __init
+cris_init_signal(void)
+{
+ u16* data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+ /* This is movu.w __NR_sigreturn, r9; break 13; */
+ data[0] = 0x9c5f;
+ data[1] = __NR_sigreturn;
+ data[2] = 0xe93d;
+ /* This is movu.w __NR_rt_sigreturn, r9; break 13; */
+ data[3] = 0x9c5f;
+ data[4] = __NR_rt_sigreturn;
+ data[5] = 0xe93d;
+
+ /* Map to userspace with appropriate permissions (no write access...) */
+ cris_signal_return_page = (unsigned long)
+ __ioremap_prot(virt_to_phys(data), PAGE_SIZE, PAGE_SIGNAL_TRAMPOLINE);
+
+ return 0;
+}
+
+__initcall(cris_init_signal);
diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c
new file mode 100644
index 000000000..4fce9f1f7
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/time.c
@@ -0,0 +1,342 @@
+/*
+ * linux/arch/cris/arch-v32/kernel/time.c
+ *
+ * Copyright (C) 2003-2010 Axis Communications AB
+ *
+ */
+
+#include <linux/timex.h>
+#include <linux/time.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/swap.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/threads.h>
+#include <linux/cpufreq.h>
+#include <linux/sched_clock.h>
+#include <linux/mm.h>
+#include <asm/types.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/irq.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 */
+/* Number of 763 counts before watchdog bites */
+#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1)
+
+#define CRISV32_TIMER_FREQ (100000000lu)
+
+unsigned long timer_regs[NR_CPUS] =
+{
+ regi_timer0,
+};
+
+extern int set_rtc_mmss(unsigned long nowtime);
+
+#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_timer0, r_tmr0_data);
+ ns = (TIMER0_DIV - data) * 10;
+ return ns;
+}
+
+/* From timer MDS describing the hardware watchdog:
+ * 4.3.1 Watchdog Operation
+ * The watchdog timer is an 8-bit timer with a configurable start value.
+ * Once started the watchdog counts downwards with a frequency of 763 Hz
+ * (100/131072 MHz). When the watchdog counts down to 1, it generates an
+ * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the
+ * chip.
+ */
+/* 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 */
+#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 set this really low. */
+#define WATCHDOG_MIN_FREE_PAGES 8
+
+#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
+/* for reliable NICE_DOGGY behaviour */
+static int bite_in_progress;
+#endif
+
+void reset_watchdog(void)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
+
+#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
+ if (unlikely(bite_in_progress))
+ return;
+#endif
+ /* 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 */
+ /* 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_timer0, rw_wd_ctrl, wd_ctrl);
+ }
+#endif
+}
+
+/* stop the watchdog - we still need the correct key */
+
+void stop_watchdog(void)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
+ watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */
+ wd_ctrl.cnt = ETRAX_WD_CNT;
+ wd_ctrl.cmd = regk_timer_stop;
+ wd_ctrl.key = watchdog_key;
+ REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
+#endif
+}
+
+extern void show_registers(struct pt_regs *regs);
+
+void handle_watchdog_bite(struct pt_regs *regs)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ extern int cause_of_death;
+
+ nmi_enter();
+ oops_in_progress = 1;
+#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
+ bite_in_progress = 1;
+#endif
+ 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. */
+ stop_watchdog();
+ printk(KERN_WARNING "Oops: bitten by watchdog\n");
+ show_registers(regs);
+ oops_in_progress = 0;
+ printk("\n"); /* Flush mtdoops. */
+#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ reset_watchdog();
+#endif
+ while(1) /* nothing */;
+#endif
+}
+
+extern void cris_profile_sample(struct pt_regs *regs);
+static void __iomem *timer_base;
+
+static void crisv32_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+ reg_timer_rw_tmr0_ctrl ctrl = {
+ .op = regk_timer_hold,
+ .freq = regk_timer_f100,
+ };
+
+ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
+}
+
+static int crisv32_clkevt_next_event(unsigned long evt,
+ struct clock_event_device *dev)
+{
+ reg_timer_rw_tmr0_ctrl ctrl = {
+ .op = regk_timer_ld,
+ .freq = regk_timer_f100,
+ };
+
+ REG_WR(timer, timer_base, rw_tmr0_div, evt);
+ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
+
+ ctrl.op = regk_timer_run;
+ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
+
+ return 0;
+}
+
+static irqreturn_t crisv32_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ reg_timer_rw_tmr0_ctrl ctrl = {
+ .op = regk_timer_hold,
+ .freq = regk_timer_f100,
+ };
+ reg_timer_rw_ack_intr ack = { .tmr0 = 1 };
+ reg_timer_r_masked_intr intr;
+
+ intr = REG_RD(timer, timer_base, r_masked_intr);
+ if (!intr.tmr0)
+ return IRQ_NONE;
+
+ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
+ REG_WR(timer, timer_base, rw_ack_intr, ack);
+
+ reset_watchdog();
+#ifdef CONFIG_SYSTEM_PROFILER
+ cris_profile_sample(get_irq_regs());
+#endif
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct clock_event_device crisv32_clockevent = {
+ .name = "crisv32-timer",
+ .rating = 300,
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = crisv32_clkevt_mode,
+ .set_next_event = crisv32_clkevt_next_event,
+};
+
+/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */
+static struct irqaction irq_timer = {
+ .handler = crisv32_timer_interrupt,
+ .flags = IRQF_TIMER | IRQF_SHARED,
+ .name = "crisv32-timer",
+ .dev_id = &crisv32_clockevent,
+};
+
+static u64 notrace crisv32_timer_sched_clock(void)
+{
+ return REG_RD(timer, timer_base, r_time);
+}
+
+static void __init crisv32_timer_init(void)
+{
+ reg_timer_rw_intr_mask timer_intr_mask;
+ reg_timer_rw_tmr0_ctrl ctrl = {
+ .op = regk_timer_hold,
+ .freq = regk_timer_f100,
+ };
+
+ REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
+
+ timer_intr_mask = REG_RD(timer, timer_base, rw_intr_mask);
+ timer_intr_mask.tmr0 = 1;
+ REG_WR(timer, timer_base, rw_intr_mask, timer_intr_mask);
+}
+
+void __init time_init(void)
+{
+ int irq;
+ int ret;
+
+ /* Probe for the RTC and read it if it exists.
+ * Before the RTC can be probed the loops_per_usec variable needs
+ * to be initialized to make usleep work. A better value for
+ * loops_per_usec is calculated by the kernel later once the
+ * clock has started.
+ */
+ loops_per_usec = 50;
+
+ irq = TIMER0_INTR_VECT;
+ timer_base = (void __iomem *) regi_timer0;
+
+ crisv32_timer_init();
+
+ sched_clock_register(crisv32_timer_sched_clock, 32,
+ CRISV32_TIMER_FREQ);
+
+ clocksource_mmio_init(timer_base + REG_RD_ADDR_timer_r_time,
+ "crisv32-timer", CRISV32_TIMER_FREQ,
+ 300, 32, clocksource_mmio_readl_up);
+
+ crisv32_clockevent.cpumask = cpu_possible_mask;
+ crisv32_clockevent.irq = irq;
+
+ ret = setup_irq(irq, &irq_timer);
+ if (ret)
+ pr_warn("failed to setup irq %d\n", irq);
+
+ clockevents_config_and_register(&crisv32_clockevent,
+ CRISV32_TIMER_FREQ,
+ 2, 0xffffffff);
+
+ /* Enable watchdog if we should use one. */
+
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ 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). */
+ {
+ 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
new file mode 100644
index 000000000..8bbe09c93
--- /dev/null
+++ b/arch/cris/arch-v32/kernel/traps.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2003-2006, Axis Communications AB.
+ */
+
+#include <linux/ptrace.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#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 process for all cases this
+ * function is called, and current->thread.usp isn't up to date for the
+ * current process. Experience shows that using USP is the way to go.
+ */
+ unsigned long usp = rdusp();
+ unsigned long d_mmu_cause;
+ unsigned long i_mmu_cause;
+
+ 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);
+
+ printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+
+ printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+
+ printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
+ regs->r8, regs->r9, regs->r10, regs->r11);
+
+ printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
+ regs->r12, regs->r13, regs->orig_r10, regs->acr);
+
+ printk(" sp: %08lx\n", (unsigned long)regs);
+
+ SUPP_BANK_SEL(BANK_IM);
+ SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);
+
+ SUPP_BANK_SEL(BANK_DM);
+ SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);
+
+ printk(" Data MMU Cause: %08lx\n", d_mmu_cause);
+ printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
+
+ printk("Process %s (pid: %d, stackpage=%08lx)\n",
+ current->comm, current->pid, (unsigned long)current);
+
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (!user_mode(regs)) {
+ int i;
+
+ show_stack(NULL, (unsigned long *)usp);
+
+ /*
+ * If the previous stack-dump wasn't a kernel one, dump the
+ * kernel stack now.
+ */
+ if (usp != 0)
+ show_stack(NULL, NULL);
+
+ printk("\nCode: ");
+
+ if (regs->erp < PAGE_OFFSET)
+ goto bad_value;
+
+ /*
+ * Quite often the value at regs->erp doesn't point to the
+ * interesting instruction, which often is the previous
+ * instruction. So dump at an offset large enough that the
+ * instruction decoding should be in sync at the interesting
+ * point, but small enough to fit on a row. The regs->erp
+ * location is pointed out in a ksymoops-friendly way by
+ * wrapping the byte for that address in parenthesises.
+ */
+ for (i = -12; i < 12; i++) {
+ unsigned char c;
+
+ if (__get_user(c, &((unsigned char *)regs->erp)[i])) {
+bad_value:
+ printk(" Bad IP value.");
+ break;
+ }
+
+ if (i == 0)
+ printk("(%02x) ", c);
+ else
+ printk("%02x ", c);
+ }
+ printk("\n");
+ }
+}
+
+void arch_enable_nmi(void)
+{
+ unsigned long flags;
+
+ local_save_flags(flags);
+ flags |= (1 << 30); /* NMI M flag is at bit 30 */
+ local_irq_restore(flags);
+}
+
+extern void (*nmi_handler)(struct pt_regs *);
+void handle_nmi(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAXFS
+ reg_intr_vect_r_nmi r;
+#endif
+
+ 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
+}
+
+
+#ifdef CONFIG_BUG
+extern void die_if_kernel(const char *str, struct pt_regs *regs, long err);
+
+/* Copy of the regs at BUG() time. */
+struct pt_regs BUG_regs;
+
+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 fixup_BUG(struct pt_regs *regs)
+{
+ 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