aboutsummaryrefslogtreecommitdiff
path: root/arch/cris/arch-v10/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/cris/arch-v10/kernel')
-rw-r--r--arch/cris/arch-v10/kernel/Makefile17
-rw-r--r--arch/cris/arch-v10/kernel/asm-offsets.c47
-rw-r--r--arch/cris/arch-v10/kernel/crisksyms.c17
-rw-r--r--arch/cris/arch-v10/kernel/debugport.c531
-rw-r--r--arch/cris/arch-v10/kernel/entry.S1132
-rw-r--r--arch/cris/arch-v10/kernel/fasttimer.c977
-rw-r--r--arch/cris/arch-v10/kernel/head.S882
-rw-r--r--arch/cris/arch-v10/kernel/irq.c204
-rw-r--r--arch/cris/arch-v10/kernel/kgdb.c1568
-rw-r--r--arch/cris/arch-v10/kernel/process.c270
-rw-r--r--arch/cris/arch-v10/kernel/ptrace.c314
-rw-r--r--arch/cris/arch-v10/kernel/setup.c103
-rw-r--r--arch/cris/arch-v10/kernel/shadows.c36
-rw-r--r--arch/cris/arch-v10/kernel/signal.c580
-rw-r--r--arch/cris/arch-v10/kernel/time.c369
-rw-r--r--arch/cris/arch-v10/kernel/traps.c132
16 files changed, 7179 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/kernel/Makefile b/arch/cris/arch-v10/kernel/Makefile
new file mode 100644
index 00000000000..52761603b6a
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.5 2004/06/02 08:24:38 starvik Exp $
+#
+# Makefile for the linux kernel.
+#
+
+extra-y := head.o
+
+
+obj-y := entry.o traps.o shadows.o debugport.o irq.o \
+ process.o setup.o signal.o traps.o time.o ptrace.o
+
+obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
+obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
+obj-$(CONFIG_MODULES) += crisksyms.o
+
+clean:
+
diff --git a/arch/cris/arch-v10/kernel/asm-offsets.c b/arch/cris/arch-v10/kernel/asm-offsets.c
new file mode 100644
index 00000000000..1aa3cc4e710
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/asm-offsets.c
@@ -0,0 +1,47 @@
+#include <linux/sched.h>
+#include <asm/thread_info.h>
+
+/*
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed to extract
+ * and format the required data.
+ */
+
+#define DEFINE(sym, val) \
+ asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+int main(void)
+{
+#define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry))
+ ENTRY(orig_r10);
+ ENTRY(r13);
+ ENTRY(r12);
+ ENTRY(r11);
+ ENTRY(r10);
+ ENTRY(r9);
+ ENTRY(mof);
+ ENTRY(dccr);
+ ENTRY(srp);
+ BLANK();
+#undef ENTRY
+#define ENTRY(entry) DEFINE(TI_ ## entry, offsetof(struct thread_info, entry))
+ ENTRY(task);
+ ENTRY(flags);
+ ENTRY(preempt_count);
+ BLANK();
+#undef ENTRY
+#define ENTRY(entry) DEFINE(THREAD_ ## entry, offsetof(struct thread_struct, entry))
+ ENTRY(ksp);
+ ENTRY(usp);
+ ENTRY(dccr);
+ BLANK();
+#undef ENTRY
+#define ENTRY(entry) DEFINE(TASK_ ## entry, offsetof(struct task_struct, entry))
+ ENTRY(pid);
+ BLANK();
+ DEFINE(LCLONE_VM, CLONE_VM);
+ DEFINE(LCLONE_UNTRACED, CLONE_UNTRACED);
+ return 0;
+}
diff --git a/arch/cris/arch-v10/kernel/crisksyms.c b/arch/cris/arch-v10/kernel/crisksyms.c
new file mode 100644
index 00000000000..b332bf9b312
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/crisksyms.c
@@ -0,0 +1,17 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/arch/svinto.h>
+
+/* Export shadow registers for the CPU I/O pins */
+EXPORT_SYMBOL(genconfig_shadow);
+EXPORT_SYMBOL(port_pa_data_shadow);
+EXPORT_SYMBOL(port_pa_dir_shadow);
+EXPORT_SYMBOL(port_pb_data_shadow);
+EXPORT_SYMBOL(port_pb_dir_shadow);
+EXPORT_SYMBOL(port_pb_config_shadow);
+EXPORT_SYMBOL(port_g_data_shadow);
+
+/* Cache flush functions */
+EXPORT_SYMBOL(flush_etrax_cache);
+EXPORT_SYMBOL(prepare_rx_descriptor);
diff --git a/arch/cris/arch-v10/kernel/debugport.c b/arch/cris/arch-v10/kernel/debugport.c
new file mode 100644
index 00000000000..6cf069e5e7b
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/debugport.c
@@ -0,0 +1,531 @@
+/* Serialport functions for debugging
+ *
+ * Copyright (c) 2000 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * Exports:
+ * console_print_etrax(char *buf)
+ * int getDebugChar()
+ * putDebugChar(int)
+ * enableDebugIRQ()
+ * init_etrax_debug()
+ *
+ * $Log: debugport.c,v $
+ * Revision 1.19 2004/10/21 07:26:16 starvik
+ * Made it possible to specify console settings on kernel command line.
+ *
+ * Revision 1.18 2004/10/19 13:07:37 starvik
+ * Merge of Linux 2.6.9
+ *
+ * Revision 1.17 2004/09/29 10:33:46 starvik
+ * Resolved a dealock when printing debug from kernel.
+ *
+ * Revision 1.16 2004/08/24 06:12:19 starvik
+ * Whitespace cleanup
+ *
+ * Revision 1.15 2004/08/16 12:37:19 starvik
+ * Merge of Linux 2.6.8
+ *
+ * Revision 1.14 2004/05/17 13:11:29 starvik
+ * Disable DMA until real serial driver is up
+ *
+ * Revision 1.13 2004/05/14 07:58:01 starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.12 2003/09/11 07:29:49 starvik
+ * Merge of Linux 2.6.0-test5
+ *
+ * Revision 1.11 2003/07/07 09:53:36 starvik
+ * Revert all the 2.5.74 merge changes to make the console work again
+ *
+ * Revision 1.9 2003/02/17 17:07:23 starvik
+ * Solved the problem with corrupted debug output (from Linux 2.4)
+ * * Wait until DMA, FIFO and pipe is empty before and after transmissions
+ * * Buffer data until a FIFO flush can be triggered.
+ *
+ * Revision 1.8 2003/01/22 06:48:36 starvik
+ * Fixed warnings issued by GCC 3.2.1
+ *
+ * Revision 1.7 2002/12/12 08:26:32 starvik
+ * Don't use C-comments inside CVS comments
+ *
+ * Revision 1.6 2002/12/11 15:42:02 starvik
+ * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/
+ *
+ * Revision 1.5 2002/11/20 06:58:03 starvik
+ * Compiles with kgdb
+ *
+ * Revision 1.4 2002/11/19 14:35:24 starvik
+ * Changes from linux 2.4
+ * Changed struct initializer syntax to the currently prefered notation
+ *
+ * Revision 1.3 2002/11/06 09:47:03 starvik
+ * Modified for new interrupt macros
+ *
+ * Revision 1.2 2002/01/21 15:21:50 bjornw
+ * Update for kdev_t changes
+ *
+ * Revision 1.6 2001/04/17 13:58:39 orjanf
+ * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
+ *
+ * Revision 1.5 2001/03/26 14:22:05 bjornw
+ * Namechange of some config options
+ *
+ * Revision 1.4 2000/10/06 12:37:26 bjornw
+ * Use physical addresses when talking to DMA
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <asm/system.h>
+#include <asm/arch/svinto.h>
+#include <asm/io.h> /* Get SIMCOUT. */
+
+struct dbg_port
+{
+ unsigned int index;
+ const volatile unsigned* read;
+ volatile char* write;
+ volatile unsigned* xoff;
+ volatile char* baud;
+ volatile char* tr_ctrl;
+ volatile char* rec_ctrl;
+ unsigned long irq;
+ unsigned int started;
+ unsigned long baudrate;
+ unsigned char parity;
+ unsigned int bits;
+};
+
+struct dbg_port ports[]=
+{
+ {
+ 0,
+ R_SERIAL0_READ,
+ R_SERIAL0_TR_DATA,
+ R_SERIAL0_XOFF,
+ R_SERIAL0_BAUD,
+ R_SERIAL0_TR_CTRL,
+ R_SERIAL0_REC_CTRL,
+ IO_STATE(R_IRQ_MASK1_SET, ser0_data, set)
+ },
+ {
+ 1,
+ R_SERIAL1_READ,
+ R_SERIAL1_TR_DATA,
+ R_SERIAL1_XOFF,
+ R_SERIAL1_BAUD,
+ R_SERIAL1_TR_CTRL,
+ R_SERIAL1_REC_CTRL,
+ IO_STATE(R_IRQ_MASK1_SET, ser1_data, set)
+ },
+ {
+ 2,
+ R_SERIAL2_READ,
+ R_SERIAL2_TR_DATA,
+ R_SERIAL2_XOFF,
+ R_SERIAL2_BAUD,
+ R_SERIAL2_TR_CTRL,
+ R_SERIAL2_REC_CTRL,
+ IO_STATE(R_IRQ_MASK1_SET, ser2_data, set)
+ },
+ {
+ 3,
+ R_SERIAL3_READ,
+ R_SERIAL3_TR_DATA,
+ R_SERIAL3_XOFF,
+ R_SERIAL3_BAUD,
+ R_SERIAL3_TR_CTRL,
+ R_SERIAL3_REC_CTRL,
+ IO_STATE(R_IRQ_MASK1_SET, ser3_data, set)
+ }
+};
+
+static struct tty_driver *serial_driver;
+
+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];
+#else
+ NULL;
+#endif
+/* Used by serial.c to register a debug_write_function so that the normal
+ * serial driver is used for kernel debug output
+ */
+typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
+
+debugport_write_function debug_write_function = NULL;
+
+static void
+start_port(void)
+{
+ unsigned long rec_ctrl = 0;
+ unsigned long tr_ctrl = 0;
+
+ if (!port)
+ return;
+
+ if (port->started)
+ return;
+ port->started = 1;
+
+ if (port->index == 0)
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+ }
+ else if (port->index == 1)
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+ }
+ else if (port->index == 2)
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, ser2, select);
+ }
+ else
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, ser3, select);
+ }
+
+ *R_GEN_CONFIG = genconfig_shadow;
+
+ *port->xoff =
+ IO_STATE(R_SERIAL0_XOFF, tx_stop, enable) |
+ IO_STATE(R_SERIAL0_XOFF, auto_xoff, disable) |
+ IO_FIELD(R_SERIAL0_XOFF, xoff_char, 0);
+
+ switch (port->baudrate)
+ {
+ case 0:
+ case 115200:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
+ break;
+ case 1200:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz);
+ break;
+ case 2400:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz);
+ break;
+ case 4800:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz);
+ break;
+ case 9600:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz);
+ break;
+ case 19200:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz);
+ break;
+ case 38400:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz);
+ break;
+ case 57600:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz);
+ break;
+ default:
+ *port->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
+ break;
+ }
+
+ if (port->parity == 'E') {
+ rec_ctrl =
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
+ tr_ctrl =
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
+ } else if (port->parity == 'O') {
+ rec_ctrl =
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
+ tr_ctrl =
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd) |
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
+ } else {
+ rec_ctrl =
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, disable);
+ tr_ctrl =
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, disable);
+ }
+
+ if (port->bits == 7)
+ {
+ rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
+ tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
+ }
+ else
+ {
+ rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_8bit);
+ tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit);
+ }
+
+ *port->rec_ctrl =
+ IO_STATE(R_SERIAL0_REC_CTRL, dma_err, stop) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rts_, active) |
+ IO_STATE(R_SERIAL0_REC_CTRL, sampling, middle) |
+ IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, normal) |
+ rec_ctrl;
+
+ *port->tr_ctrl =
+ IO_FIELD(R_SERIAL0_TR_CTRL, txd, 0) |
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable) |
+ IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, disabled) |
+ IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, one_bit) |
+ IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, normal) |
+ tr_ctrl;
+}
+
+static void
+console_write_direct(struct console *co, const char *buf, unsigned int len)
+{
+ int i;
+ unsigned long flags;
+ local_irq_save(flags);
+ /* Send data */
+ for (i = 0; i < len; i++) {
+ /* Wait until transmitter is ready and send.*/
+ while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+ ;
+ *port->write = buf[i];
+ }
+ local_irq_restore(flags);
+}
+
+static void
+console_write(struct console *co, const char *buf, unsigned int len)
+{
+ if (!port)
+ return;
+
+#ifdef CONFIG_SVINTO_SIM
+ /* no use to simulate the serial debug output */
+ SIMCOUT(buf, len);
+ return;
+#endif
+
+ start_port();
+
+#ifdef CONFIG_ETRAX_KGDB
+ /* kgdb needs to output debug info using the gdb protocol */
+ putDebugString(buf, len);
+ return;
+#endif
+
+ if (debug_write_function)
+ debug_write_function(co->index, buf, len);
+ else
+ console_write_direct(co, buf, len);
+}
+
+/* legacy function */
+
+void
+console_print_etrax(const char *buf)
+{
+ console_write(NULL, buf, strlen(buf));
+}
+
+/* Use polling to get a single character FROM the debug port */
+
+int
+getDebugChar(void)
+{
+ unsigned long readval;
+
+ do {
+ readval = *port->read;
+ } while (!(readval & IO_MASK(R_SERIAL0_READ, data_avail)));
+
+ return (readval & IO_MASK(R_SERIAL0_READ, data_in));
+}
+
+/* Use polling to put a single character to the debug port */
+
+void
+putDebugChar(int val)
+{
+ while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+ ;
+ *port->write = val;
+}
+
+/* Enable irq for receiving chars on the debug port, used by kgdb */
+
+void
+enableDebugIRQ(void)
+{
+ *R_IRQ_MASK1_SET = port->irq;
+ /* use R_VECT_MASK directly, since we really bypass Linux normal
+ * IRQ handling in kgdb anyway, we don't need to use enable_irq
+ */
+ *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+
+ *port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
+}
+
+static struct tty_driver*
+etrax_console_device(struct console* co, int *index)
+{
+ return serial_driver;
+}
+
+static int __init
+console_setup(struct console *co, char *options)
+{
+ char* s;
+
+ if (options) {
+ port = &ports[co->index];
+ port->baudrate = 115200;
+ port->parity = 'N';
+ port->bits = 8;
+ port->baudrate = simple_strtoul(options, NULL, 10);
+ s = options;
+ while(*s >= '0' && *s <= '9')
+ s++;
+ if (*s) port->parity = *s++;
+ if (*s) port->bits = *s++ - '0';
+ port->started = 0;
+ start_port();
+ }
+ return 0;
+}
+
+static struct console sercons = {
+ name : "ttyS",
+ write: console_write,
+ read : NULL,
+ device : etrax_console_device,
+ unblank : NULL,
+ setup : console_setup,
+ flags : CON_PRINTBUFFER,
+ index : -1,
+ cflag : 0,
+ next : NULL
+};
+static struct console sercons0 = {
+ name : "ttyS",
+ write: console_write,
+ read : NULL,
+ device : etrax_console_device,
+ unblank : NULL,
+ setup : console_setup,
+ flags : CON_PRINTBUFFER,
+ index : 0,
+ cflag : 0,
+ next : NULL
+};
+
+static struct console sercons1 = {
+ name : "ttyS",
+ write: console_write,
+ read : NULL,
+ device : etrax_console_device,
+ unblank : NULL,
+ setup : console_setup,
+ flags : CON_PRINTBUFFER,
+ index : 1,
+ cflag : 0,
+ next : NULL
+};
+static struct console sercons2 = {
+ name : "ttyS",
+ write: console_write,
+ read : NULL,
+ device : etrax_console_device,
+ unblank : NULL,
+ setup : console_setup,
+ flags : CON_PRINTBUFFER,
+ index : 2,
+ cflag : 0,
+ next : NULL
+};
+static struct console sercons3 = {
+ name : "ttyS",
+ write: console_write,
+ read : NULL,
+ device : etrax_console_device,
+ unblank : NULL,
+ setup : console_setup,
+ flags : CON_PRINTBUFFER,
+ index : 3,
+ cflag : 0,
+ next : NULL
+};
+/*
+ * Register console (for printk's etc)
+ */
+
+int __init
+init_etrax_debug(void)
+{
+ static int first = 1;
+
+ if (!first) {
+ if (!port) {
+ register_console(&sercons0);
+ register_console(&sercons1);
+ register_console(&sercons2);
+ register_console(&sercons3);
+ unregister_console(&sercons);
+ }
+ return 0;
+ }
+ first = 0;
+ if (port)
+ register_console(&sercons);
+ return 0;
+}
+
+int __init
+init_console(void)
+{
+ serial_driver = alloc_tty_driver(1);
+ if (!serial_driver)
+ return -ENOMEM;
+ return 0;
+}
+
+__initcall(init_etrax_debug);
diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S
new file mode 100644
index 00000000000..1bc44f481c3
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/entry.S
@@ -0,0 +1,1132 @@
+/* $Id: entry.S,v 1.23 2004/10/19 13:07:37 starvik Exp $
+ *
+ * linux/arch/cris/entry.S
+ *
+ * Copyright (C) 2000, 2001, 2002 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * $Log: entry.S,v $
+ * Revision 1.23 2004/10/19 13:07:37 starvik
+ * Merge of Linux 2.6.9
+ *
+ * Revision 1.22 2004/06/21 10:29:55 starvik
+ * Merge of Linux 2.6.7
+ *
+ * Revision 1.21 2004/06/09 05:30:27 starvik
+ * Clean up multiple interrupt handling.
+ * Prevent interrupts from interrupting each other.
+ * Handle all active interrupts.
+ *
+ * Revision 1.20 2004/06/08 08:55:32 starvik
+ * Removed unused code
+ *
+ * Revision 1.19 2004/06/04 11:56:15 starvik
+ * Implemented page table lookup for refills in assembler for improved performance.
+ *
+ * Revision 1.18 2004/05/11 12:28:25 starvik
+ * Merge of Linux 2.6.6
+ *
+ * Revision 1.17 2003/09/11 07:29:49 starvik
+ * Merge of Linux 2.6.0-test5
+ *
+ * Revision 1.16 2003/07/04 08:27:41 starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.15 2003/04/09 07:32:55 starvik
+ * resume should return task_struct, not thread_info
+ *
+ * Revision 1.14 2003/04/09 05:20:44 starvik
+ * Merge of Linux 2.5.67
+ *
+ * Revision 1.13 2002/12/11 15:42:02 starvik
+ * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c
+ *
+ * Revision 1.12 2002/12/10 09:00:10 starvik
+ * Merge of Linux 2.5.51
+ *
+ * Revision 1.11 2002/12/05 07:53:10 starvik
+ * Corrected constants used with btstq
+ *
+ * Revision 1.10 2002/11/27 08:45:10 starvik
+ * pid is in task_struct, not thread_info
+ *
+ * Revision 1.9 2002/11/26 09:52:05 starvik
+ * Added preemptive kernel scheduling (if CONFIG_PREEMPT)
+ *
+ * Revision 1.8 2002/11/20 11:56:11 starvik
+ * Merge of Linux 2.5.48
+ *
+ * Revision 1.7 2002/11/18 13:02:42 starvik
+ * Added fourth parameter to do_notify_resume
+ * Minor cleanup
+ *
+ * Revision 1.6 2002/11/11 10:37:50 starvik
+ * Use new asm-offset defines
+ * Modified for new location of current->work etc
+ * Removed SYMBOL_NAME from syscalls
+ * Added some new syscalls
+ *
+ * Revision 1.5 2002/11/05 06:45:11 starvik
+ * Merge of Linux 2.5.45
+ *
+ * Revision 1.4 2002/02/05 15:41:31 bjornw
+ * Rewritten to conform better to current 2.5 code (similar to arch/i386)
+ *
+ * Revision 1.3 2002/01/21 15:22:20 bjornw
+ * NICE_DOGGY fix from 2.4 arch/cris
+ *
+ * Revision 1.37 2001/12/07 17:03:55 bjornw
+ * Call a c-hook called watchdog_bite_hook instead of show_registers directly
+ *
+ * Revision 1.36 2001/11/22 13:36:36 bjornw
+ * * In ret_from_intr, check regs->dccr for usermode reentrance instead of
+ * DCCR explicitely (because the latter might not reflect current reality)
+ * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before
+ * since $r9 is call-clobbered and is potentially needed afterwards
+ *
+ * Revision 1.35 2001/10/30 17:10:15 bjornw
+ * Add some syscalls
+ *
+ * Revision 1.34 2001/10/01 14:45:03 bjornw
+ * Removed underscores and added register prefixes
+ *
+ * Revision 1.33 2001/08/21 13:48:01 jonashg
+ * Added fix by HP to avoid oops when doing a hard_reset_now.
+ *
+ * Revision 1.32 2001/08/14 04:32:02 hp
+ * In _resume, add comment why R9 is saved; don't sound like it's call-saved.
+ *
+ * Revision 1.31 2001/07/25 16:07:42 bjornw
+ * softirq_active/mask -> softirq_pending only
+ *
+ * Revision 1.30 2001/07/05 01:03:32 hp
+ * - include asm/errno.h to get ENOSYS.
+ * - Use ENOSYS, not local constant LENOSYS; tweak comments.
+ * - Explain why .include, not #include is used.
+ * - Make oops-register-dump if watchdog bits and it's not expected.
+ * - Don't jsr, use jump _hard_reset_now, and skip spurious nop.
+ * - Use correct section attribute for section .rodata.
+ * - Adjust sys_ni_syscall fill number.
+ *
+ * Revision 1.29 2001/06/25 14:07:00 hp
+ * Fix review comment.
+ * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of
+ * magic numbers. Add comment that -traditional must not be used.
+ * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation.
+ * Correct and update comment.
+ * * Makefile (.S.o): Don't use -traditional. Add comment why the
+ * toplevel rule can't be used (now that there's a reason).
+ *
+ * Revision 1.28 2001/06/21 02:00:40 hp
+ * * entry.S: Include asm/unistd.h.
+ * (_sys_call_table): Use section .rodata, not .data.
+ * (_kernel_thread): Move from...
+ * * process.c: ... here.
+ * * entryoffsets.c (VAL): Break out from...
+ * (OF): Use VAL.
+ * (LCLONE_VM): New asmified value from CLONE_VM.
+ *
+ * Revision 1.27 2001/05/29 11:25:27 markusl
+ * In case of "spurious_interrupt", do hard_reset instead of hanging system in a loop...
+ *
+ * Revision 1.26 2001/05/15 15:46:03 bjornw
+ * Include config.h now that we use some CONFIG_ options
+ *
+ * Revision 1.25 2001/05/15 05:38:47 hp
+ * Tweaked code in _ret_from_sys_call
+ *
+ * Revision 1.24 2001/05/15 05:27:49 hp
+ * Save r9 in r1 over function call rather than on stack.
+ *
+ * Revision 1.23 2001/05/15 05:10:00 hp
+ * Generate entry.S structure offsets from C
+ *
+ * Revision 1.22 2001/04/17 13:58:39 orjanf
+ * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
+ *
+ * Revision 1.21 2001/04/17 11:33:29 orjanf
+ * Updated according to review:
+ * * Included asm/sv_addr_ag.h to get macro for internal register.
+ * * Corrected comment regarding system call argument passing.
+ * * Removed comment about instruction being in a delay slot.
+ * * Added comment about SYMBOL_NAME macro.
+ *
+ * Revision 1.20 2001/04/12 08:51:07 hp
+ * - Add entry for sys_fcntl64. In fact copy last piece from i386 including ...
+ * - .rept to fill table to safe state with sys_ni_syscall.
+ *
+ * Revision 1.19 2001/04/04 09:43:32 orjanf
+ * * Moved do_sigtrap from traps.c to entry.S.
+ * * LTASK_PID need not be global anymore.
+ *
+ * Revision 1.18 2001/03/26 09:25:02 markusl
+ * Updated after review, should now handle USB interrupts correctly.
+ *
+ * Revision 1.17 2001/03/21 16:12:55 bjornw
+ * * Always make room for the cpu status record in the frame, in order to
+ * use the same framelength and layout for both mmu busfaults and normal
+ * irqs. No need to check for the explicit CRIS_FRAME_FIXUP type anymore.
+ * * Fixed bug with using addq for popping the stack in the epilogue - it
+ * destroyed the flag register. Use instructions that don't affect the
+ * flag register instead.
+ * * Removed write to R_PORT_PA_DATA during spurious_interrupt
+ *
+ * Revision 1.16 2001/03/20 19:43:02 bjornw
+ * * Get rid of esp0 setting
+ * * Give a 7th argument to a systemcall - the stackframe
+ *
+ * Revision 1.15 2001/03/05 13:14:30 bjornw
+ * Spelling fix
+ *
+ * Revision 1.14 2001/02/23 08:36:36 perf
+ * New ABI; syscallnr=r9, arg5=mof, arg6=srp.
+ * Corrected tracesys call check.
+ *
+ * Revision 1.13 2001/02/15 08:40:55 perf
+ * H-P by way of perf;
+ * - (_system_call): Don't read system call function address into r1.
+ * - (RBFExit): There is no such thing as a null pop. Adjust sp by addq.
+ * - (_system_call): Don't use r10 and don't save and restore it.
+ * - (THREAD_ESP0): New constant.
+ * - (_system_call): Inline set_esp0.
+ *
+ * Revision 1.12 2001/01/31 17:56:25 orjanf
+ * Added definition of LTASK_PID and made it global.
+ *
+ * Revision 1.11 2001/01/10 21:13:29 bjornw
+ * SYMBOL_NAME is defined incorrectly for the compiler options we currently use
+ *
+ * Revision 1.10 2000/12/18 23:47:56 bjornw
+ * * Added syscall trace support (ptrace), completely untested of course
+ * * Removed redundant check for NULL entries in syscall_table
+ *
+ * Revision 1.9 2000/11/21 16:40:51 bjornw
+ * * New frame type used when an SBFS frame needs to be popped without
+ * actually restarting the instruction
+ * * Enable interrupts in signal_return (they did so in x86, I hope it's a good
+ * idea)
+ *
+ * Revision 1.8 2000/11/17 16:53:35 bjornw
+ * Added detection of frame-type in Rexit, so that mmu_bus_fault can
+ * use ret_from_intr in the return-path to check for signals (like SEGV)
+ * and other foul things that might have occurred during the fault.
+ *
+ * Revision 1.7 2000/10/06 15:04:28 bjornw
+ * Include mof in register savings
+ *
+ * Revision 1.6 2000/09/12 16:02:44 bjornw
+ * Linux-2.4.0-test7 derived updates
+ *
+ * Revision 1.5 2000/08/17 15:35:15 bjornw
+ * 2.4.0-test6 changed local_irq_count and friends API
+ *
+ * Revision 1.4 2000/08/02 13:59:30 bjornw
+ * Removed olduname and uname from the syscall list
+ *
+ * Revision 1.3 2000/07/31 13:32:58 bjornw
+ * * Export ret_from_intr
+ * * _resume updated (prev/last tjohejsan)
+ * * timer_interrupt obsolete
+ * * SIGSEGV detection in mmu_bus_fault temporarily disabled
+ *
+ *
+ */
+
+/*
+ * entry.S contains 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/config.h>
+#include <linux/linkage.h>
+#include <linux/sys.h>
+#include <asm/unistd.h>
+#include <asm/arch/sv_addr_ag.h>
+#include <asm/errno.h>
+#include <asm/thread_info.h>
+#include <asm/arch/offset.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+ ;; functions exported from this file
+
+ .globl system_call
+ .globl ret_from_intr
+ .globl ret_from_fork
+ .globl resume
+ .globl multiple_interrupt
+ .globl hwbreakpoint
+ .globl IRQ1_interrupt
+ .globl spurious_interrupt
+ .globl hw_bp_trigs
+ .globl mmu_bus_fault
+ .globl do_sigtrap
+ .globl gdb_handle_breakpoint
+ .globl sys_call_table
+
+ ;; below are various parts of system_call which are not in the fast-path
+
+#ifdef CONFIG_PREEMPT
+ ; Check if preemptive kernel scheduling should be done
+_resume_kernel:
+ ; Load current task struct
+ movs.w -8192, $r0 ; THREAD_SIZE = 8192
+ and.d $sp, $r0
+ move.d [$r0+TI_preempt_count], $r10 ; Preemption disabled?
+ bne _Rexit
+ nop
+_need_resched:
+ move.d [$r0+TI_flags], $r10
+ btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set
+ bpl _Rexit
+ nop
+ ; Ok, lets's do some preemptive kernel scheduling
+ move.d PREEMPT_ACTIVE, $r10
+ move.d $r10, [$r0+TI_preempt_count] ; Mark as active
+ ei
+ jsr schedule
+ clear.d [$r0+TI_preempt_count] ; Mark as inactive
+ di
+ ; 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
+ret_from_fork:
+ jsr schedule_tail
+ ba ret_from_sys_call
+ nop
+
+ret_from_intr:
+ ;; check for resched if preemptive kernel or if we're going back to user-mode
+ ;; this test matches the user_regs(regs) macro
+ ;; we cannot simply test $dccr, because that does not necessarily
+ ;; reflect what mode we'll return into.
+
+ move.d [$sp + PT_dccr], $r0; regs->dccr
+ btstq 8, $r0 ; U-flag
+ bpl _resume_kernel
+ ; Note that di below is in delay slot
+
+_resume_userspace:
+ di ; so need_resched and sigpending don't change
+
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ move.d [$r0+TI_flags], $r10 ; current->work
+ and.d _TIF_WORK_MASK, $r10 ; is there any work to be done on return
+ bne _work_pending
+ nop
+ ba _Rexit
+ nop
+
+ ;; The system_call is called by a BREAK instruction, which works like
+ ;; an interrupt call but it stores the return PC in BRP instead of IRP.
+ ;; Since we dont really want to have two epilogues (one for system calls
+ ;; and one for interrupts) we push the contents of BRP instead of IRP in the
+ ;; system call prologue, to make it look like an ordinary interrupt on the
+ ;; stackframe.
+ ;;
+ ;; Since we can't have system calls inside interrupts, it should not matter
+ ;; that we don't stack IRP.
+ ;;
+ ;; 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..
+
+system_call:
+ ;; stack-frame similar to the irq heads, which is reversed in ret_from_sys_call
+ move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
+ push $srp
+ push $dccr
+ push $mof
+ subq 14*4, $sp ; make room for r0-r13
+ movem $r13, [$sp] ; push r0-r13
+ push $r10 ; push orig_r10
+ clear.d [$sp=$sp-4] ; frametype == 0, normal stackframe
+
+ movs.w -ENOSYS, $r0
+ move.d $r0, [$sp+PT_r10] ; put the default return value in r10 in the frame
+
+ ;; check if this process is syscall-traced
+
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ move.d [$r0+TI_flags], $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
+ bcc ret_from_sys_call
+ lslq 2, $r9 ; multiply by 4, in the delay slot
+
+ ;; as a bonus 7th parameter, we give the location on the stack
+ ;; of the register structure itself. some syscalls need this.
+
+ push $sp
+
+ ;; the parameter carrying registers r10, r11, r12 and 13 are intact.
+ ;; the fifth and sixth parameters (if any) was in mof and srp
+ ;; respectively, and we need to put them on the stack.
+
+ push $srp
+ push $mof
+
+ jsr [$r9+sys_call_table] ; actually do the system call
+ addq 3*4, $sp ; pop the mof, srp and regs parameters
+ move.d $r10, [$sp+PT_r10] ; 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 - if >=1 we came from a syscall, if 0, from an irq
+
+ ;; get the current task-struct pointer (see top for defs)
+
+ movs.w -8192, $r0 ; THREAD_SIZE == 8192
+ and.d $sp, $r0
+
+ di ; make sure need_resched and sigpending don't change
+ move.d [$r0+TI_flags],$r1
+ and.d _TIF_ALLWORK_MASK, $r1
+ bne _syscall_exit_work
+ nop
+
+_Rexit:
+ ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h
+ pop $r10 ; frametype
+ bne _RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise
+ addq 4, $sp ; skip orig_r10, in delayslot
+ movem [$sp+], $r13 ; registers r0-r13
+ pop $mof ; multiply overflow register
+ pop $dccr ; condition codes
+ pop $srp ; subroutine return pointer
+ ;; now we have a 4-word SBFS frame which we do not want to restore
+ ;; using RBF since it was not stacked with SBFS. instead we would like to
+ ;; just get the PC value to restart it with, and skip the rest of
+ ;; the frame.
+ ;; Also notice that it's important to use instructions here that
+ ;; keep the interrupts disabled (since we've already popped DCCR)
+ move [$sp=$sp+16], $p8; pop the SBFS frame from the sp
+ jmpu [$sp-16] ; return through the irp field in the sbfs frame
+
+_RBFexit:
+ movem [$sp+], $r13 ; registers r0-r13, in delay slot
+ pop $mof ; multiply overflow register
+ pop $dccr ; condition codes
+ pop $srp ; subroutine return pointer
+ rbf [$sp+] ; return by popping the CPU status
+
+ ;; We get here after doing a syscall if extra work might need to be done
+ ;; perform syscall exit tracing if needed
+
+_syscall_exit_work:
+ ;; $r0 contains current at this point and irq's are disabled
+
+ move.d [$r0+TI_flags], $r1
+ btstq TIF_SYSCALL_TRACE, $r1
+ bpl _work_pending
+ nop
+
+ ei
+
+ move.d $r9, $r1 ; preserve r9
+ jsr do_syscall_trace
+ move.d $r1, $r9
+
+ ba _resume_userspace
+ nop
+
+_work_pending:
+ move.d [$r0+TI_flags], $r1
+ btstq TIF_NEED_RESCHED, $r1
+ bpl _work_notifysig ; was neither trace nor sched, must be signal/notify
+ nop
+
+_work_resched:
+ move.d $r9, $r1 ; preserve r9
+ jsr schedule
+ move.d $r1, $r9
+ di
+
+ move.d [$r0+TI_flags], $r1
+ and.d _TIF_WORK_MASK, $r1; ignore the syscall trace counter
+ beq _Rexit
+ nop
+ btstq TIF_NEED_RESCHED, $r1
+ bmi _work_resched ; current->work.need_resched
+ nop
+
+_work_notifysig:
+ ;; deal with pending signals and notify-resume requests
+
+ move.d $r9, $r10 ; do_notify_resume syscall/irq param
+ moveq 0, $r11 ; oldset param - 0 in this case
+ move.d $sp, $r12 ; the regs param
+ move.d $r1, $r13 ; the thread_info_flags parameter
+ jsr do_notify_resume
+
+ ba _Rexit
+ nop
+
+ ;; We get here as a sidetrack when we've entered a syscall with the
+ ;; trace-bit set. We need to call do_syscall_trace and then continue
+ ;; with the call.
+
+_syscall_trace_entry:
+ ;; PT_r10 in the frame contains -ENOSYS as required, at this point
+
+ jsr do_syscall_trace
+
+ ;; 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
+
+ move.d [$sp+PT_r9], $r9
+ move.d [$sp+PT_orig_r10], $r10 ; PT_r10 is already filled with -ENOSYS.
+ move.d [$sp+PT_r11], $r11
+ move.d [$sp+PT_r12], $r12
+ move.d [$sp+PT_r13], $r13
+ move [$sp+PT_mof], $mof
+ move [$sp+PT_srp], $srp
+
+ ba _syscall_traced
+ nop
+
+ ;; resume performs the actual task-switching, by switching stack pointers
+ ;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct
+ ;; returns old current in r10
+ ;;
+ ;; TODO: see the i386 version. The switch_to which calls resume in our version
+ ;; could really be an inline asm of this.
+
+resume:
+ push $srp ; we keep the old/new PC on the stack
+ add.d $r12, $r10 ; r10 = current tasks tss
+ move $dccr, [$r10+THREAD_dccr]; save irq enable state
+ di
+
+ move $usp, [$r10+ THREAD_usp] ; save user-mode stackpointer
+
+ ;; See copy_thread for the reason why register R9 is saved.
+ subq 10*4, $sp
+ movem $r9, [$sp] ; save non-scratch registers and R9.
+
+ move.d $sp, [$r10+THREAD_ksp] ; save the kernel stack pointer for the old task
+ move.d $sp, $r10 ; return last running task in r10
+ and.d -8192, $r10 ; get thread_info from stackpointer
+ move.d [$r10+TI_task], $r10 ; get task
+ add.d $r12, $r11 ; find the new tasks tss
+ move.d [$r11+THREAD_ksp], $sp ; switch into the new stackframe by restoring kernel sp
+
+ movem [$sp+], $r9 ; restore non-scratch registers and R9.
+
+ move [$r11+THREAD_usp], $usp ; restore user-mode stackpointer
+
+ move [$r11+THREAD_dccr], $dccr ; restore irq enable status
+ jump [$sp+] ; restore PC
+
+ ;; This is the MMU bus fault handler.
+ ;; It needs to stack the CPU status and overall is different
+ ;; from the other interrupt handlers.
+
+mmu_bus_fault:
+ ;; For refills we try to do a quick page table lookup. If it is
+ ;; a real fault we let the mm subsystem handle it.
+
+ ;; the first longword in the sbfs frame was the interrupted PC
+ ;; which fits nicely with the "IRP" slot in pt_regs normally used to
+ ;; contain the return address. used by Oops to print kernel errors.
+ sbfs [$sp=$sp-16] ; push the internal CPU status
+ push $dccr
+ di
+ subq 2*4, $sp
+ movem $r1, [$sp]
+ move.d [R_MMU_CAUSE], $r1
+ ;; ETRAX 100LX TR89 bugfix: if the second half of an unaligned
+ ;; write causes a MMU-fault, it will not be restarted correctly.
+ ;; This could happen if a write crosses a page-boundary and the
+ ;; second page is not yet COW'ed or even loaded. The workaround
+ ;; is to clear the unaligned bit in the CPU status record, so
+ ;; that the CPU will rerun both the first and second halves of
+ ;; the instruction. This will not have any sideeffects unless
+ ;; the first half goes to any device or memory that can't be
+ ;; written twice, and which is mapped through the MMU.
+ ;;
+ ;; We only need to do this for writes.
+ btstq 8, $r1 ; Write access?
+ bpl 1f
+ nop
+ move.d [$sp+16], $r0 ; Clear unaligned bit in csrinstr
+ and.d ~(1<<5), $r0
+ move.d $r0, [$sp+16]
+1: btstq 12, $r1 ; Refill?
+ bpl 2f
+ lsrq PMD_SHIFT, $r1 ; Get PMD index into PGD (bit 24-31)
+ move.d [current_pgd], $r0 ; PGD for the current process
+ move.d [$r0+$r1.d], $r0 ; Get PMD
+ beq 2f
+ nop
+ and.w PAGE_MASK, $r0 ; Remove PMD flags
+ move.d [R_MMU_CAUSE], $r1
+ lsrq PAGE_SHIFT, $r1
+ and.d 0x7ff, $r1 ; Get PTE index into PMD (bit 13-24)
+ move.d [$r0+$r1.d], $r1 ; Get PTE
+ beq 2f
+ nop
+ ;; Store in TLB
+ move.d $r1, [R_TLB_LO]
+ ;; Return
+ movem [$sp+], $r1
+ pop $dccr
+ rbf [$sp+] ; return by popping the CPU status
+
+2: ; PMD or PTE missing, let the mm subsystem fix it up.
+ movem [$sp+], $r1
+ pop $dccr
+
+ ; Ok, not that easy, pass it on to the mm subsystem
+ ; The MMU status record is now on the stack
+ push $srp ; make a stackframe similar to pt_regs
+ push $dccr
+ push $mof
+ di
+ subq 14*4, $sp
+ movem $r13, [$sp]
+ push $r10 ; dummy orig_r10
+ moveq 1, $r10
+ push $r10 ; frametype == 1, BUSFAULT frame type
+
+ move.d $sp, $r10 ; pt_regs argument to handle_mmu_bus_fault
+
+ jsr handle_mmu_bus_fault ; in arch/cris/arch-v10/mm/fault.c
+
+ ;; now we need to return through the normal path, we cannot just
+ ;; do the RBFexit since we might have killed off the running
+ ;; process due to a SEGV, scheduled due to a page blocking or
+ ;; whatever.
+
+ moveq 0, $r9 ; busfault is equivalent to an irq
+
+ ba ret_from_intr
+ nop
+
+ ;; special handlers for breakpoint and NMI
+hwbreakpoint:
+ push $dccr
+ di
+ push $r10
+ push $r11
+ move.d [hw_bp_trig_ptr],$r10
+ move $brp,$r11
+ move.d $r11,[$r10+]
+ move.d $r10,[hw_bp_trig_ptr]
+1: pop $r11
+ pop $r10
+ pop $dccr
+ retb
+ nop
+
+IRQ1_interrupt:
+
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+;; 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!!!
+ move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
+ push $srp
+ push $dccr
+ push $mof
+ di
+ subq 14*4, $sp
+ movem $r13, [$sp]
+ push $r10 ; push orig_r10
+ clear.d [$sp=$sp-4] ; frametype == 0, normal frame
+
+;; We don't check that we actually were bit by the watchdog as opposed to
+;; an external NMI, since there is currently no handler for external NMI.
+
+;; Check if we're waiting for reset to happen, as signalled by
+;; hard_reset_now setting cause_of_death to a magic value. If so, just
+;; get stuck until reset happens.
+ .comm cause_of_death, 4 ;; Don't declare this anywhere.
+ move.d [cause_of_death], $r10
+ cmp.d 0xbedead, $r10
+_killed_by_death:
+ beq _killed_by_death
+ nop
+
+;; We'll see this in ksymoops dumps.
+Watchdog_bite:
+
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ ;; We just restart the watchdog here to be sure we dont get
+ ;; hit while printing the watchdogmsg below
+ ;; This restart is compatible with the rest of the C-code, so
+ ;; the C-code can keep restarting the watchdog after this point.
+ ;; The non-NICE_DOGGY code below though, disables the possibility
+ ;; to restart since it changes the watchdog key, to avoid any
+ ;; buggy loops etc. keeping the watchdog alive after this.
+ jsr reset_watchdog
+#else
+
+;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have
+;; time for an oops-dump over a 115k2 serial wire. Another 100ms should do.
+
+;; Change the watchdog key to an arbitrary 3-bit value and restart the
+;; watchdog.
+#define WD_INIT 2
+ moveq IO_FIELD (R_WATCHDOG, key, WD_INIT), $r10
+ move.d R_WATCHDOG, $r11
+
+ move.d $r10, [$r11]
+ moveq IO_FIELD (R_WATCHDOG, key, \
+ IO_EXTRACT (R_WATCHDOG, key, \
+ IO_MASK (R_WATCHDOG, key)) \
+ ^ WD_INIT) \
+ | IO_STATE (R_WATCHDOG, enable, start), $r10
+ move.d $r10, [$r11]
+
+#endif
+
+;; Note that we don't do "setf m" here (or after two necessary NOPs),
+;; since *not* doing that saves us from re-entrancy checks. We don't want
+;; to get here again due to possible subsequent NMIs; we want the watchdog
+;; to reset us.
+
+ move.d _watchdogmsg,$r10
+ jsr printk
+
+ move.d $sp, $r10
+ jsr watchdog_bite_hook
+
+;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
+;; rather than "spurious_interrupt".
+ nop
+;; At this point we drop down into spurious_interrupt, which will do a
+;; hard reset.
+
+ .section .rodata,"a"
+_watchdogmsg:
+ .ascii "Oops: bitten by watchdog\n\0"
+ .previous
+
+#endif /* CONFIG_ETRAX_WATCHDOG and not CONFIG_SVINTO_SIM */
+
+spurious_interrupt:
+ di
+ jump hard_reset_now
+
+ ;; this handles the case when multiple interrupts arrive at the same time
+ ;; we jump to the first set interrupt bit in a priority fashion
+ ;; the hardware will call the unserved interrupts after the handler finishes
+
+multiple_interrupt:
+ ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
+ move $irp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
+ push $srp
+ push $dccr
+ push $mof
+ di
+ subq 14*4, $sp
+ movem $r13, [$sp]
+ push $r10 ; push orig_r10
+ clear.d [$sp=$sp-4] ; frametype == 0, normal frame
+
+ moveq 2, $r2 ; first bit we care about is the timer0 irq
+ move.d [R_VECT_MASK_RD], $r0; read the irq bits that triggered the multiple irq
+ move.d $r0, [R_VECT_MASK_CLR] ; Block all active IRQs
+1:
+ btst $r2, $r0 ; check for the irq given by bit r2
+ bpl 2f
+ move.d $r2, $r10 ; First argument to do_IRQ
+ move.d $sp, $r11 ; second argument to do_IRQ
+ jsr do_IRQ
+2:
+ addq 1, $r2 ; next vector bit
+ cmp.b 32, $r2
+ bne 1b ; process all irq's up to and including number 31
+ moveq 0, $r9 ; make ret_from_intr realise we came from an ir
+
+ move.d $r0, [R_VECT_MASK_SET] ; Unblock all the IRQs
+ jump ret_from_intr
+
+do_sigtrap:
+ ;;
+ ;; SIGTRAP the process that executed the break instruction.
+ ;; Make a frame that Rexit in entry.S expects.
+ ;;
+ move $brp, [$sp=$sp-16] ; Push BRP while faking a cpu status record.
+ push $srp ; Push subroutine return pointer.
+ push $dccr ; Push condition codes.
+ push $mof ; Push multiply overflow reg.
+ 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.
+ push $r10 ; Push orig_r10.
+ clear.d [$sp=$sp-4] ; Frametype - this is a normal stackframe.
+
+ movs.w -8192,$r9 ; THREAD_SIZE == 8192
+ and.d $sp, $r9
+ move.d [$r9+TI_task], $r10
+ move.d [$r10+TASK_pid], $r10 ; current->pid as arg1.
+ moveq 5, $r11 ; SIGTRAP as arg2.
+ jsr sys_kill
+ jump ret_from_intr ; Use the return routine for interrupts.
+
+gdb_handle_breakpoint:
+ push $dccr
+ push $r0
+#ifdef CONFIG_ETRAX_KGDB
+ move $dccr, $r0 ; U-flag not affected by previous insns.
+ btstq 8, $r0 ; Test the U-flag.
+ bmi _ugdb_handle_breakpoint ; Go to user mode debugging.
+ nop ; Empty delay slot (cannot pop r0 here).
+ pop $r0 ; Restore r0.
+ ba kgdb_handle_breakpoint ; Go to kernel debugging.
+ pop $dccr ; Restore dccr in delay slot.
+#endif
+
+_ugdb_handle_breakpoint:
+ move $brp, $r0 ; Use r0 temporarily for calculation.
+ subq 2, $r0 ; Set to address of previous instruction.
+ move $r0, $brp
+ pop $r0 ; Restore r0.
+ ba do_sigtrap ; SIGTRAP the offending process.
+ pop $dccr ; Restore dccr in delay slot.
+
+ .data
+
+hw_bp_trigs:
+ .space 64*4
+hw_bp_trig_ptr:
+ .dword hw_bp_trigs
+
+ .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 old_readdir
+ .long 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_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
+
+ /*
+ * 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-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c
new file mode 100644
index 00000000000..4717f7ae8e5
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/fasttimer.c
@@ -0,0 +1,977 @@
+/* $Id: fasttimer.c,v 1.6 2004/05/14 10:18:39 starvik Exp $
+ * linux/arch/cris/kernel/fasttimer.c
+ *
+ * Fast timers for ETRAX100/ETRAX100LX
+ * This may be useful in other OS than Linux so use 2 space indentation...
+ *
+ * $Log: fasttimer.c,v $
+ * Revision 1.6 2004/05/14 10:18:39 starvik
+ * Export fast_timer_list
+ *
+ * Revision 1.5 2004/05/14 07:58:01 starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.4 2003/07/04 08:27:41 starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.3 2002/12/12 08:26:32 starvik
+ * Don't use C-comments inside CVS comments
+ *
+ * Revision 1.2 2002/12/11 15:42:02 starvik
+ * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/
+ *
+ * Revision 1.1 2002/11/18 07:58:06 starvik
+ * Fast timers (from Linux 2.4)
+ *
+ * Revision 1.5 2002/10/15 06:21:39 starvik
+ * Added call to init_waitqueue_head
+ *
+ * Revision 1.4 2002/05/28 17:47:59 johana
+ * Added del_fast_timer()
+ *
+ * Revision 1.3 2002/05/28 16:16:07 johana
+ * Handle empty fast_timer_list
+ *
+ * Revision 1.2 2002/05/27 15:38:42 johana
+ * Made it compile without warnings on Linux 2.4.
+ * (includes, wait_queue, PROC_FS and snprintf)
+ *
+ * Revision 1.1 2002/05/27 15:32:25 johana
+ * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree.
+ *
+ * Revision 1.8 2001/11/27 13:50:40 pkj
+ * Disable interrupts while stopping the timer and while modifying the
+ * list of active timers in timer1_handler() as it may be interrupted
+ * by other interrupts (e.g., the serial interrupt) which may add fast
+ * timers.
+ *
+ * Revision 1.7 2001/11/22 11:50:32 pkj
+ * * Only store information about the last 16 timers.
+ * * proc_fasttimer_read() now uses an allocated buffer, since it
+ * requires more space than just a page even for only writing the
+ * last 16 timers. The buffer is only allocated on request, so
+ * unless /proc/fasttimer is read, it is never allocated.
+ * * Renamed fast_timer_started to fast_timers_started to match
+ * fast_timers_added and fast_timers_expired.
+ * * Some clean-up.
+ *
+ * Revision 1.6 2000/12/13 14:02:08 johana
+ * Removed volatile for fast_timer_list
+ *
+ * Revision 1.5 2000/12/13 13:55:35 johana
+ * Added DEBUG_LOG, added som cli() and cleanup
+ *
+ * Revision 1.4 2000/12/05 13:48:50 johana
+ * Added range check when writing proc file, modified timer int handling
+ *
+ * Revision 1.3 2000/11/23 10:10:20 johana
+ * More debug/logging possibilities.
+ * Moved GET_JIFFIES_USEC() to timex.h and time.c
+ *
+ * Revision 1.2 2000/11/01 13:41:04 johana
+ * Clean up and bugfixes.
+ * Created new do_gettimeofday_fast() that gets a timeval struct
+ * with time based on jiffies and *R_TIMER0_DATA, uses a table
+ * for fast conversion of timer value to microseconds.
+ * (Much faster the standard do_gettimeofday() and we don't really
+ * wan't to use the true time - we wan't the "uptime" so timers don't screw up
+ * when we change the time.
+ * TODO: Add efficient support for continuous timers as well.
+ *
+ * Revision 1.1 2000/10/26 15:49:16 johana
+ * Added fasttimer, highresolution timers.
+ *
+ * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/rtc.h>
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <asm/arch/svinto.h>
+#include <asm/fasttimer.h>
+#include <linux/proc_fs.h>
+
+
+#define DEBUG_LOG_INCLUDED
+#define FAST_TIMER_LOG
+//#define FAST_TIMER_TEST
+
+#define FAST_TIMER_SANITY_CHECKS
+
+#ifdef FAST_TIMER_SANITY_CHECKS
+#define SANITYCHECK(x) x
+static int sanity_failed = 0;
+#else
+#define SANITYCHECK(x)
+#endif
+
+#define D1(x)
+#define D2(x)
+#define DP(x)
+
+#define __INLINE__ inline
+
+static int fast_timer_running = 0;
+static int fast_timers_added = 0;
+static int fast_timers_started = 0;
+static int fast_timers_expired = 0;
+static int fast_timers_deleted = 0;
+static int fast_timer_is_init = 0;
+static int fast_timer_ints = 0;
+
+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 int debug_log_cnt = 0;
+static int debug_log_cnt_wrapped = 0;
+
+#define DEBUG_LOG(string, value) \
+{ \
+ unsigned long log_flags; \
+ save_flags(log_flags); \
+ cli(); \
+ 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; \
+ } \
+ restore_flags(log_flags); \
+}
+#else
+#define DEBUG_LOG(string, value)
+#endif
+
+
+/* The frequencies for index = clkselx number in R_TIMER_CTRL */
+#define NUM_TIMER_FREQ 15
+#define MAX_USABLE_TIMER_FREQ 7
+#define MAX_DELAY_US 853333L
+const unsigned long timer_freq_100[NUM_TIMER_FREQ] =
+{
+ 3, /* 0 3333 - 853333 us */
+ 6, /* 1 1666 - 426666 us */
+ 12, /* 2 833 - 213333 us */
+ 24, /* 3 416 - 106666 us */
+ 48, /* 4 208 - 53333 us */
+ 96, /* 5 104 - 26666 us */
+ 192, /* 6 52 - 13333 us */
+ 384, /* 7 26 - 6666 us */
+ 576,
+ 1152,
+ 2304,
+ 4608,
+ 9216,
+ 18432,
+ 62500,
+ /* 15 = cascade */
+};
+#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_freq_settings[NUM_TIMER_STATS];
+int timer_delay_settings[NUM_TIMER_STATS];
+
+/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
+void __INLINE__ do_gettimeofday_fast(struct timeval *tv)
+{
+ unsigned long sec = jiffies;
+ unsigned long usec = GET_JIFFIES_USEC();
+
+ usec += (sec % HZ) * (1000000 / HZ);
+ sec = sec / HZ;
+
+ if (usec > 1000000)
+ {
+ usec -= 1000000;
+ sec++;
+ }
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1)
+{
+ if (t0->tv_sec < t1->tv_sec)
+ {
+ return -1;
+ }
+ else if (t0->tv_sec > t1->tv_sec)
+ {
+ return 1;
+ }
+ if (t0->tv_usec < t1->tv_usec)
+ {
+ return -1;
+ }
+ else if (t0->tv_usec > t1->tv_usec)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+void __INLINE__ start_timer1(unsigned long delay_us)
+{
+ int freq_index = 0; /* This is the lowest resolution */
+ unsigned long upper_limit = MAX_DELAY_US;
+
+ unsigned long div;
+ /* Start/Restart the timer to the new shorter value */
+ /* t = 1/freq = 1/19200 = 53us
+ * T=div*t, div = T/t = delay_us*freq/1000000
+ */
+#if 1 /* Adaptive timer settings */
+ while (delay_us < upper_limit && freq_index < MAX_USABLE_TIMER_FREQ)
+ {
+ freq_index++;
+ upper_limit >>= 1; /* Divide by 2 using shift */
+ }
+ if (freq_index > 0)
+ {
+ freq_index--;
+ }
+#else
+ freq_index = 6;
+#endif
+ div = delay_us * timer_freq_100[freq_index]/10000;
+ if (div < 2)
+ {
+ /* Maybe increase timer freq? */
+ div = 2;
+ }
+ if (div > 255)
+ {
+ div = 0; /* This means 256, the max the timer takes */
+ /* If a longer timeout than the timer can handle is used,
+ * then we must restart it when it goes off.
+ */
+ }
+
+ timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = div;
+ timer_freq_settings[fast_timers_started % NUM_TIMER_STATS] = freq_index;
+ timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
+
+ D1(printk("start_timer1 : %d us freq: %i div: %i\n",
+ delay_us, freq_index, div));
+ /* Clear timer1 irq */
+ *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
+
+ /* Set timer values */
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ (r_timer_ctrl_shadow &
+ ~IO_MASK(R_TIMER_CTRL, timerdiv1) &
+ ~IO_MASK(R_TIMER_CTRL, tm1) &
+ ~IO_MASK(R_TIMER_CTRL, clksel1)) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, div) |
+ IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
+ IO_FIELD(R_TIMER_CTRL, clksel1, freq_index ); /* 6=c19k2Hz */
+
+ /* Ack interrupt */
+ *R_TIMER_CTRL = r_timer_ctrl_shadow |
+ IO_STATE(R_TIMER_CTRL, i1, clr);
+
+ /* Start timer */
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) |
+ IO_STATE(R_TIMER_CTRL, tm1, run);
+
+ /* Enable timer1 irq */
+ *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set);
+ fast_timers_started++;
+ fast_timer_running = 1;
+}
+
+/* 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));
+
+ save_flags(flags);
+ cli();
+
+ do_gettimeofday_fast(&t->tv_set);
+ tmp = fast_timer_list;
+
+ SANITYCHECK({ /* Check so this is not in the list already... */
+ while (tmp != NULL)
+ {
+ if (tmp == t)
+ {
+ printk(KERN_WARNING
+ "timer name: %s data: 0x%08lX already in list!\n", name, data);
+ sanity_failed++;
+ return;
+ }
+ else
+ {
+ tmp = tmp->next;
+ }
+ }
+ tmp = fast_timer_list;
+ });
+
+ 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_sec = t->tv_set.tv_sec + delay_us / 1000000;
+ if (t->tv_expires.tv_usec > 1000000)
+ {
+ t->tv_expires.tv_usec -= 1000000;
+ t->tv_expires.tv_sec++;
+ }
+#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 || timeval_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_timer1(delay_us);
+ } else {
+ /* Put in correct place in list */
+ while (tmp->next &&
+ timeval_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));
+
+ restore_flags(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;
+
+ save_flags(flags);
+ cli();
+ ret = detach_fast_timer(t);
+ t->next = t->prev = NULL;
+ restore_flags(flags);
+ return ret;
+} /* del_fast_timer */
+
+
+/* Interrupt routines or functions called in interrupt context */
+
+/* Timer 1 interrupt handler */
+
+static irqreturn_t
+timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct fast_timer *t;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ /* Clear timer1 irq */
+ *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
+
+ /* First stop timer, then ack interrupt */
+ /* Stop timer */
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) |
+ IO_STATE(R_TIMER_CTRL, tm1, stop_ld);
+
+ /* Ack interrupt */
+ *R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i1, clr);
+
+ fast_timer_running = 0;
+ fast_timer_ints++;
+
+ restore_flags(flags);
+
+ t = fast_timer_list;
+ while (t)
+ {
+ struct timeval tv;
+
+ /* Has it really expired? */
+ do_gettimeofday_fast(&tv);
+ D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec));
+
+ if (timeval_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 */
+ save_flags(flags);
+ cli();
+ 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;
+ restore_flags(flags);
+
+ if (t->function != NULL)
+ {
+ t->function(t->data);
+ }
+ else
+ {
+ DEBUG_LOG("!timer1 %i function==NULL!\n", fast_timer_ints);
+ }
+ }
+ else
+ {
+ /* Timer is to early, let's set it again using the normal routines */
+ D1(printk(".\n"));
+ }
+
+ save_flags(flags);
+ cli();
+ if ((t = fast_timer_list) != NULL)
+ {
+ /* Start next timer.. */
+ long us;
+ struct timeval tv;
+
+ do_gettimeofday_fast(&tv);
+ us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 +
+ t->tv_expires.tv_usec - tv.tv_usec);
+ if (us > 0)
+ {
+ if (!fast_timer_running)
+ {
+#ifdef FAST_TIMER_LOG
+ timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
+#endif
+ start_timer1(us);
+ }
+ restore_flags(flags);
+ break;
+ }
+ else
+ {
+ /* Timer already expired, let's handle it better late than never.
+ * The normal loop handles it
+ */
+ D1(printk("e! %d\n", us));
+ }
+ }
+ restore_flags(flags);
+ }
+
+ if (!t)
+ {
+ D1(printk("t1 stop!\n"));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void wake_up_func(unsigned long data)
+{
+#ifdef DECLARE_WAITQUEUE
+ wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
+#else
+ struct wait_queue **sleep_wait_p = (struct wait_queue **)data;
+#endif
+ wake_up(sleep_wait_p);
+}
+
+
+/* Useful API */
+
+void schedule_usleep(unsigned long us)
+{
+ struct fast_timer t;
+#ifdef DECLARE_WAITQUEUE
+ wait_queue_head_t sleep_wait;
+ init_waitqueue_head(&sleep_wait);
+ {
+ DECLARE_WAITQUEUE(wait, current);
+#else
+ struct wait_queue *sleep_wait = NULL;
+ struct wait_queue wait = { current, NULL };
+#endif
+
+ D1(printk("schedule_usleep(%d)\n", us));
+ add_wait_queue(&sleep_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
+ "usleep");
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&sleep_wait, &wait);
+ D1(printk("done schedule_usleep(%d)\n", us));
+#ifdef DECLARE_WAITQUEUE
+ }
+#endif
+}
+
+#ifdef CONFIG_PROC_FS
+static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
+ ,int *eof, void *data_unused);
+static struct proc_dir_entry *fasttimer_proc_entry;
+#endif /* CONFIG_PROC_FS */
+
+#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_read(char *buf, char **start, off_t offset, int len
+ ,int *eof, void *data_unused)
+{
+ unsigned long flags;
+ int i = 0;
+ int num_to_show;
+ struct timeval tv;
+ struct fast_timer *t, *nextt;
+ static char *bigbuf = NULL;
+ static unsigned long used;
+
+ if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE)))
+ {
+ used = 0;
+ bigbuf[0] = '\0';
+ return 0;
+ }
+
+ if (!offset || !used)
+ {
+ do_gettimeofday_fast(&tv);
+
+ used = 0;
+ used += sprintf(bigbuf + used, "Fast timers added: %i\n",
+ fast_timers_added);
+ used += sprintf(bigbuf + used, "Fast timers started: %i\n",
+ fast_timers_started);
+ used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n",
+ fast_timer_ints);
+ used += sprintf(bigbuf + used, "Fast timers expired: %i\n",
+ fast_timers_expired);
+ used += sprintf(bigbuf + used, "Fast timers deleted: %i\n",
+ fast_timers_deleted);
+ used += sprintf(bigbuf + used, "Fast timer running: %s\n",
+ fast_timer_running ? "yes" : "no");
+ used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n",
+ (unsigned long)tv.tv_sec,
+ (unsigned long)tv.tv_usec);
+#ifdef FAST_TIMER_SANITY_CHECKS
+ used += sprintf(bigbuf + used, "Sanity failed: %i\n",
+ sanity_failed);
+#endif
+ used += sprintf(bigbuf + used, "\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 && !used)) &&
+ used+100 < BIG_BUF_SIZE)
+ {
+ used += sprintf(bigbuf + used, debug_log_string[i],
+ debug_log_value[i]);
+ i = (i+1) % DEBUG_LOG_MAX;
+ }
+ }
+ used += sprintf(bigbuf + used, "\n");
+#endif
+
+ num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
+ NUM_TIMER_STATS);
+ used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started);
+ for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++)
+ {
+ int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
+
+#if 1 //ndef FAST_TIMER_LOG
+ used += sprintf(bigbuf + used, "div: %i freq: %i delay: %i"
+ "\n",
+ timer_div_settings[cur],
+ timer_freq_settings[cur],
+ timer_delay_settings[cur]
+ );
+#endif
+#ifdef FAST_TIMER_LOG
+ t = &timer_started_log[cur];
+ used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
+ "d: %6li us data: 0x%08lX"
+ "\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_sec,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_sec,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data
+ );
+#endif
+ }
+ used += sprintf(bigbuf + used, "\n");
+
+#ifdef FAST_TIMER_LOG
+ num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
+ NUM_TIMER_STATS);
+ used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added);
+ for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
+ {
+ t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
+ used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
+ "d: %6li us data: 0x%08lX"
+ "\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_sec,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_sec,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data
+ );
+ }
+ used += sprintf(bigbuf + used, "\n");
+
+ num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
+ NUM_TIMER_STATS);
+ used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired);
+ for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
+ {
+ t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
+ used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
+ "d: %6li us data: 0x%08lX"
+ "\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_sec,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_sec,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data
+ );
+ }
+ used += sprintf(bigbuf + used, "\n");
+#endif
+
+ used += sprintf(bigbuf + used, "Active timers:\n");
+ save_flags(flags);
+ cli();
+ t = fast_timer_list;
+ while (t != NULL && (used+100 < BIG_BUF_SIZE))
+ {
+ nextt = t->next;
+ restore_flags(flags);
+ used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
+ "d: %6li us data: 0x%08lX"
+/* " func: 0x%08lX" */
+ "\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_sec,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_sec,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data
+/* , t->function */
+ );
+ cli();
+ if (t->next != nextt)
+ {
+ printk(KERN_WARNING "timer removed!\n");
+ }
+ t = nextt;
+ }
+ restore_flags(flags);
+ }
+
+ if (used - offset < len)
+ {
+ len = used - offset;
+ }
+
+ memcpy(buf, bigbuf + offset, len);
+ *start = buf;
+ *eof = 1;
+
+ return len;
+}
+#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 timeval 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 timeval 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("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec);
+
+ for (j = 0; j < 1000; j++)
+ {
+ printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]);
+ j += 4;
+ }
+ for (j = 0; j < 100; j++)
+ {
+ printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n",
+ tv_exp[j].tv_sec,tv_exp[j].tv_usec,
+ tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec,
+ tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec,
+ tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec,
+ tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec);
+ 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("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec);
+ printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec);
+ printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec);
+ 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_sec,
+ t->tv_set.tv_usec,
+ t->tv_expires.tv_sec,
+ 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_sec,
+ tv_exp[j].tv_usec,
+ exp_num[j],
+ (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
+ }
+ proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+ printk("buf5 after all done:\n");
+ printk(buf5);
+ printk("fast_timer_test() done\n");
+}
+#endif
+
+
+void fast_timer_init(void)
+{
+ /* For some reason, request_irq() hangs when called froom time_init() */
+ if (!fast_timer_is_init)
+ {
+#if 0 && defined(FAST_TIMER_TEST)
+ int i;
+#endif
+
+ printk(KERN_INFO "fast_timer_init()\n");
+
+#if 0 && defined(FAST_TIMER_TEST)
+ for (i = 0; i <= TIMER0_DIV; i++)
+ {
+ /* We must be careful not to get overflow... */
+ printk("%3i %6u\n", i, timer0_value_us[i]);
+ }
+#endif
+#ifdef CONFIG_PROC_FS
+ if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 )))
+ fasttimer_proc_entry->read_proc = proc_fasttimer_read;
+#endif /* PROC_FS */
+ if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ,
+ "fast timer int", NULL))
+ {
+ printk("err: timer1 irq\n");
+ }
+ fast_timer_is_init = 1;
+#ifdef FAST_TIMER_TEST
+ printk("do test\n");
+ fast_timer_test();
+#endif
+ }
+}
diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S
new file mode 100644
index 00000000000..2c1dd1184a8
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/head.S
@@ -0,0 +1,882 @@
+/* $Id: head.S,v 1.7 2004/05/14 07:58:01 starvik Exp $
+ *
+ * Head of the kernel - alter with care
+ *
+ * Copyright (C) 2000, 2001 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * $Log: head.S,v $
+ * Revision 1.7 2004/05/14 07:58:01 starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.6 2003/04/28 05:31:46 starvik
+ * Added section attributes
+ *
+ * Revision 1.5 2002/12/11 15:42:02 starvik
+ * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c
+ *
+ * Revision 1.4 2002/11/07 09:00:44 starvik
+ * Names changed for init sections
+ * init_task_union -> init_thread_union
+ *
+ * Revision 1.3 2002/02/05 15:38:23 bjornw
+ * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it...
+ *
+ * Revision 1.2 2001/12/18 13:35:19 bjornw
+ * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15).
+ *
+ * Revision 1.43 2001/11/08 15:09:43 starvik
+ * Only start MII clock if Ethernet is configured
+ *
+ * Revision 1.42 2001/11/08 14:37:34 starvik
+ * Start MII clock early to make sure that it is running at tranceiver reset
+ *
+ * Revision 1.41 2001/10/29 14:55:58 pkj
+ * Corrected pa$r0 to par0.
+ *
+ * Revision 1.40 2001/10/03 14:59:57 pkj
+ * Added support for resetting the Bluetooth hardware.
+ *
+ * Revision 1.39 2001/10/01 14:45:03 bjornw
+ * Removed underscores and added register prefixes
+ *
+ * Revision 1.38 2001/09/21 07:14:11 jonashg
+ * Made root filesystem (cramfs) use mtdblock driver when booting from flash.
+ *
+ * Revision 1.37 2001/09/11 13:44:29 orjanf
+ * Decouple usage of serial ports for debug and kgdb.
+ *
+ * Revision 1.36 2001/06/29 12:39:31 pkj
+ * Added support for mirroring the first flash to just below the
+ * second one, to make them look consecutive to cramfs.
+ *
+ * Revision 1.35 2001/06/25 14:07:00 hp
+ * Fix review comment.
+ * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of
+ * magic numbers. Add comment that -traditional must not be used.
+ * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation.
+ * Correct and update comment.
+ * * Makefile (.S.o): Don't use -traditional. Add comment why the
+ * toplevel rule can't be used (now that there's a reason).
+ *
+ * Revision 1.34 2001/05/15 07:08:14 hp
+ * Tweak "notice" to reflect that both r8 r9 are used
+ *
+ * Revision 1.33 2001/05/15 06:40:05 hp
+ * Put bulk of code in .text.init, data in .data.init
+ *
+ * Revision 1.32 2001/05/15 06:18:56 hp
+ * Execute review comment: s/bcc/bhs/g; s/bcs/blo/g
+ *
+ * Revision 1.31 2001/05/15 06:08:40 hp
+ * Add sentence about autodetecting the bit31-MMU-bug
+ *
+ * Revision 1.30 2001/05/15 06:00:05 hp
+ * Update comment: LOW_MAP is not forced on xsim anymore.
+ *
+ * Revision 1.29 2001/04/18 12:51:59 orjanf
+ * * Reverted review change regarding the use of bcs/bcc.
+ * * Removed non-working LED-clearing code.
+ *
+ * Revision 1.28 2001/04/17 13:58:39 orjanf
+ * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
+ *
+ * Revision 1.27 2001/04/17 11:42:35 orjanf
+ * Changed according to review:
+ * * Added comment explaining memory map bug.
+ * * Changed bcs and bcc to blo and bhs, respectively.
+ * * Removed mentioning of Stallone and Olga boards.
+ *
+ * Revision 1.26 2001/04/06 12:31:07 jonashg
+ * Check for cramfs in flash before RAM instead of RAM before flash.
+ *
+ * Revision 1.25 2001/04/04 06:23:53 starvik
+ * Initialize DRAM if not already initialized
+ *
+ * Revision 1.24 2001/04/03 11:12:00 starvik
+ * Removed dram init (done by rescue or etrax100boot
+ * Corrected include
+ *
+ * Revision 1.23 2001/04/03 09:53:03 starvik
+ * Include hw_settings.S
+ *
+ * Revision 1.22 2001/03/26 14:23:26 bjornw
+ * Namechange of some config options
+ *
+ * Revision 1.21 2001/03/08 12:14:41 bjornw
+ * * Config name for ETRAX IDE was renamed
+ * * Removed G27 auto-setting when JULIETTE is chosen (need to make this
+ * a new config option later)
+ *
+ * Revision 1.20 2001/02/23 12:47:56 bjornw
+ * MMU regs during LOW_MAP updated to reflect a newer reality
+ *
+ * Revision 1.19 2001/02/19 11:12:07 bjornw
+ * Changed comment header format
+ *
+ * Revision 1.18 2001/02/15 07:25:38 starvik
+ * Added support for synchronous serial ports
+ *
+ * Revision 1.17 2001/02/08 15:53:13 starvik
+ * Last commit removed some important ifdefs
+ *
+ * Revision 1.16 2001/02/08 15:20:38 starvik
+ * Include dram_init.S as inline
+ *
+ * Revision 1.15 2001/01/29 18:12:01 bjornw
+ * Corrected some comments
+ *
+ * Revision 1.14 2001/01/29 13:11:29 starvik
+ * Include dram_init.S (with DRAM/SDRAM initialization)
+ *
+ * Revision 1.13 2001/01/23 14:54:57 markusl
+ * Updated for USB
+ * i.e. added r_gen_config settings
+ *
+ * Revision 1.12 2001/01/19 16:16:29 perf
+ * Added temporary mapping of 0x0c->0x0c to avoid flash loading confusion.
+ * Renamed serial options from ETRAX100 to ETRAX.
+ *
+ * Revision 1.11 2001/01/16 16:31:38 bjornw
+ * * Changed name and semantics of running_from_flash to romfs_in_flash,
+ * set by head.S to indicate to setup.c whether there is a cramfs image
+ * after the kernels BSS or not. Should work for all three boot-cases
+ * (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot),
+ * and flash with cramfs in flash)
+ *
+ * Revision 1.10 2001/01/16 14:12:21 bjornw
+ * * Check for cramfs start passed in r9 from the decompressor, if all other
+ * cramfs options fail (if we boot from DRAM but don't find a cramfs image
+ * after the kernel in DRAM, it is probably still in the flash)
+ * * Check magic in cramfs detection when booting from flash directly
+ *
+ * Revision 1.9 2001/01/15 17:17:02 bjornw
+ * * Corrected the code that detects the cramfs lengths
+ * * Added a comment saying that the above does not work due to other
+ * reasons..
+ *
+ * Revision 1.8 2001/01/15 16:27:51 jonashg
+ * Made boot after flashing work.
+ * * end destination is __vmlinux_end in RAM.
+ * * _romfs_start moved because of virtual memory.
+ *
+ * Revision 1.7 2000/11/21 13:55:29 bjornw
+ * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type
+ *
+ * Revision 1.6 2000/10/06 12:36:55 bjornw
+ * Forgot swapper_pg_dir when changing memory map..
+ *
+ * Revision 1.5 2000/10/04 16:49:30 bjornw
+ * * Fixed memory mapping in LX
+ * * Check for cramfs instead of romfs
+ *
+ */
+
+#include <linux/config.h>
+#define ASSEMBLER_MACROS_ONLY
+/* The IO_* macros use the ## token concatenation operator, so
+ -traditional must not be used when assembling this file. */
+#include <asm/arch/sv_addr_ag.h>
+
+#define CRAMFS_MAGIC 0x28cd3d45
+#define RAM_INIT_MAGIC 0x56902387
+
+#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
+ IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
+
+ ;; exported symbols
+
+ .globl etrax_irv
+ .globl romfs_start
+ .globl romfs_length
+ .globl romfs_in_flash
+ .globl swapper_pg_dir
+
+ .text
+
+ ;; This is the entry point of the kernel. We are in supervisor mode.
+ ;; 0x00000000 if Flash, 0x40004000 if DRAM
+ ;; since etrax actually starts at address 2 when booting from flash, we
+ ;; put a nop (2 bytes) here first so we dont accidentally skip the di
+ ;;
+ ;; NOTICE! The registers r8 and r9 are used as parameters carrying
+ ;; information from the decompressor (if the kernel was compressed).
+ ;; They should not be used in the code below until read.
+
+ nop
+ di
+
+ ;; First setup the kseg_c mapping from where the kernel is linked
+ ;; to 0x40000000 (where the actual DRAM resides) otherwise
+ ;; we cannot do very much! See arch/cris/README.mm
+ ;;
+ ;; Notice that since we're potentially running at 0x00 or 0x40 right now,
+ ;; we will get a fault as soon as we enable the MMU if we dont
+ ;; temporarily map those segments linearily.
+ ;;
+ ;; Due to a bug in Etrax-100 LX version 1 we need to map the memory
+ ;; slightly different. The bug is that you can't remap bit 31 of
+ ;; an address. Though we can check the version register for
+ ;; whether the bug is present, some constants would then have to
+ ;; be variables, so we don't. The drawback is that you can "only" map
+ ;; 1G per process with CONFIG_CRIS_LOW_MAP.
+
+#ifdef CONFIG_CRIS_LOW_MAP
+ ; kseg mappings, temporary map of 0xc0->0x40
+ move.d IO_FIELD (R_MMU_KBASE_HI, base_c, 4) \
+ | IO_FIELD (R_MMU_KBASE_HI, base_b, 0xb) \
+ | IO_FIELD (R_MMU_KBASE_HI, base_9, 9) \
+ | IO_FIELD (R_MMU_KBASE_HI, base_8, 8), $r0
+ move.d $r0, [R_MMU_KBASE_HI]
+
+ ; temporary map of 0x40->0x40 and 0x60->0x40
+ move.d IO_FIELD (R_MMU_KBASE_LO, base_6, 4) \
+ | IO_FIELD (R_MMU_KBASE_LO, base_4, 4), $r0
+ move.d $r0, [R_MMU_KBASE_LO]
+
+ ; mmu enable, segs e,c,b,a,6,5,4,0 segment mapped
+ move.d IO_STATE (R_MMU_CONFIG, mmu_enable, enable) \
+ | IO_STATE (R_MMU_CONFIG, inv_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, acc_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, we_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, seg_f, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_e, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_d, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_c, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_b, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_a, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_9, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_8, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_7, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_6, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_5, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_4, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_3, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_2, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_1, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_0, seg), $r0
+ move.d $r0, [R_MMU_CONFIG]
+#else
+ ; kseg mappings
+ move.d IO_FIELD (R_MMU_KBASE_HI, base_e, 8) \
+ | IO_FIELD (R_MMU_KBASE_HI, base_c, 4) \
+ | IO_FIELD (R_MMU_KBASE_HI, base_b, 0xb), $r0
+ move.d $r0, [R_MMU_KBASE_HI]
+
+ ; temporary map of 0x40->0x40 and 0x00->0x00
+ move.d IO_FIELD (R_MMU_KBASE_LO, base_4, 4), $r0
+ move.d $r0, [R_MMU_KBASE_LO]
+
+ ; mmu enable, segs f,e,c,b,4,0 segment mapped
+ move.d IO_STATE (R_MMU_CONFIG, mmu_enable, enable) \
+ | IO_STATE (R_MMU_CONFIG, inv_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, acc_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, we_excp, enable) \
+ | IO_STATE (R_MMU_CONFIG, seg_f, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_e, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_d, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_c, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_b, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_a, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_9, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_8, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_7, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_6, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_5, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_4, seg) \
+ | IO_STATE (R_MMU_CONFIG, seg_3, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_2, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_1, page) \
+ | IO_STATE (R_MMU_CONFIG, seg_0, seg), $r0
+ move.d $r0, [R_MMU_CONFIG]
+#endif
+
+ ;; Now we need to sort out the segments and their locations in RAM or
+ ;; Flash. The image in the Flash (or in DRAM) consists of 3 pieces:
+ ;; 1) kernel text, 2) kernel data, 3) ROM filesystem image
+ ;; But the linker has linked the kernel to expect this layout in
+ ;; DRAM memory:
+ ;; 1) kernel text, 2) kernel data, 3) kernel BSS
+ ;; (the location of the ROM filesystem is determined by the krom driver)
+ ;; If we boot this from Flash, we want to keep the ROM filesystem in
+ ;; the flash, we want to copy the text and need to copy the data to DRAM.
+ ;; But if we boot from DRAM, we need to move the ROMFS image
+ ;; from its position after kernel data, to after kernel BSS, BEFORE the
+ ;; kernel starts using the BSS area (since its "overlayed" with the ROMFS)
+ ;;
+ ;; In both cases, we start in un-cached mode, and need to jump into a
+ ;; cached PC after we're done fiddling around with the segments.
+ ;;
+ ;; arch/etrax100/etrax100.ld sets some symbols that define the start
+ ;; and end of each segment.
+
+ ;; Check if we start from DRAM or FLASH by testing PC
+
+ move.d $pc,$r0
+ and.d 0x7fffffff,$r0 ; get rid of the non-cache bit
+ cmp.d 0x10000,$r0 ; arbitrary... just something above this code
+ blo _inflash0
+ nop
+
+ jump _inram ; enter cached ram
+
+ ;; Jumpgate for branches.
+_inflash0:
+ jump _inflash
+
+ ;; Put this in a suitable section where we can reclaim storage
+ ;; after init.
+ .section ".init.text", "ax"
+_inflash:
+#ifdef CONFIG_ETRAX_ETHERNET
+ ;; Start MII clock to make sure it is running when tranceiver is reset
+ move.d START_ETHERNET_CLOCK, $r0
+ move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+
+ ;; Set up waitstates etc according to kernel configuration.
+#ifndef CONFIG_SVINTO_SIM
+ move.d CONFIG_ETRAX_DEF_R_WAITSTATES, $r0
+ move.d $r0, [R_WAITSTATES]
+
+ move.d CONFIG_ETRAX_DEF_R_BUS_CONFIG, $r0
+ move.d $r0, [R_BUS_CONFIG]
+#endif
+
+ ;; We need to initialze DRAM registers before we start using the DRAM
+
+ cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
+ beq _dram_init_finished
+ nop
+
+#include "../lib/dram_init.S"
+
+_dram_init_finished:
+ ;; Copy text+data to DRAM
+ ;; This is fragile - the calculation of r4 as the image size depends
+ ;; on that the labels below actually are the first and last positions
+ ;; in the linker-script.
+ ;;
+ ;; Then the locating of the cramfs image depends on the aforementioned
+ ;; image being located in the flash at 0. This is most often not true,
+ ;; thus the following does not work (normally there is a rescue-block
+ ;; between the physical start of the flash and the flash-image start,
+ ;; and when run with compression, the kernel is actually unpacked to
+ ;; DRAM and we never get here in the first place :))
+
+ moveq 0, $r0 ; source
+ move.d text_start, $r1 ; destination
+ move.d __vmlinux_end, $r2 ; end destination
+ move.d $r2, $r4
+ sub.d $r1, $r4 ; r4=__vmlinux_end in flash, used below
+1: move.w [$r0+], $r3
+ move.w $r3, [$r1+]
+ cmp.d $r2, $r1
+ blo 1b
+ nop
+
+ ;; We keep the cramfs in the flash.
+ ;; There might be none, but that does not matter because
+ ;; we don't do anything than read some bytes here.
+
+ moveq 0, $r0
+ move.d $r0, [romfs_length] ; default if there is no cramfs
+
+ move.d [$r4], $r0 ; cramfs_super.magic
+ cmp.d CRAMFS_MAGIC, $r0
+ bne 1f
+ nop
+ move.d [$r4 + 4], $r0 ; cramfs_super.size
+ move.d $r0, [romfs_length]
+#ifdef CONFIG_CRIS_LOW_MAP
+ add.d 0x50000000, $r4 ; add flash start in virtual memory (cached)
+#else
+ add.d 0xf0000000, $r4 ; add flash start in virtual memory (cached)
+#endif
+ move.d $r4, [romfs_start]
+1:
+ moveq 1, $r0
+ move.d $r0, [romfs_in_flash]
+
+ jump _start_it ; enter code, cached this time
+
+_inram:
+ ;; Move the ROM fs to after BSS end. This assumes that the cramfs
+ ;; second longword contains the length of the cramfs
+
+ moveq 0, $r0
+ move.d $r0, [romfs_length] ; default if there is no cramfs
+
+ ;; The kernel could have been unpacked to DRAM by the loader, but
+ ;; the cramfs image could still be in the Flash directly after the
+ ;; compressed kernel image. The loader passes the address of the
+ ;; byte succeeding the last compressed byte in the flash in the
+ ;; register r9 when starting the kernel. Check if r9 points to a
+ ;; decent cramfs image!
+ ;; (Notice that if this is not booted from the loader, r9 will be
+ ;; garbage but we do sanity checks on it, the chance that it points
+ ;; to a cramfs magic is small.. )
+
+ cmp.d 0x0ffffff8, $r9
+ bhs _no_romfs_in_flash ; r9 points outside the flash area
+ nop
+ move.d [$r9], $r0 ; cramfs_super.magic
+ cmp.d CRAMFS_MAGIC, $r0
+ bne _no_romfs_in_flash
+ nop
+ move.d [$r9+4], $r0 ; cramfs_super.length
+ move.d $r0, [romfs_length]
+#ifdef CONFIG_CRIS_LOW_MAP
+ add.d 0x50000000, $r9 ; add flash start in virtual memory (cached)
+#else
+ add.d 0xf0000000, $r9 ; add flash start in virtual memory (cached)
+#endif
+ move.d $r9, [romfs_start]
+
+ moveq 1, $r0
+ move.d $r0, [romfs_in_flash]
+
+ jump _start_it ; enter code, cached this time
+
+_no_romfs_in_flash:
+
+ ;; Check if there is a cramfs (magic value).
+ ;; Notice that we check for cramfs magic value - which is
+ ;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does
+ ;; not need this mechanism anyway)
+
+ move.d __vmlinux_end, $r0; the image will be after the vmlinux end address
+ move.d [$r0], $r1 ; cramfs assumes same endian on host/target
+ cmp.d CRAMFS_MAGIC, $r1; magic value in cramfs superblock
+ bne 2f
+ nop
+
+ ;; Ok. What is its size ?
+
+ move.d [$r0 + 4], $r2 ; cramfs_super.size (again, no need to swapwb)
+
+ ;; We want to copy it to the end of the BSS
+
+ move.d _end, $r1
+
+ ;; Remember values so cramfs and setup can find this info
+
+ move.d $r1, [romfs_start] ; new romfs location
+ move.d $r2, [romfs_length]
+
+ ;; We need to copy it backwards, since they can be overlapping
+
+ add.d $r2, $r0
+ add.d $r2, $r1
+
+ ;; Go ahead. Make my loop.
+
+ lsrq 1, $r2 ; size is in bytes, we copy words
+
+1: move.w [$r0=$r0-2],$r3
+ move.w $r3,[$r1=$r1-2]
+ subq 1, $r2
+ bne 1b
+ nop
+
+2:
+ ;; Dont worry that the BSS is tainted. It will be cleared later.
+
+ moveq 0, $r0
+ move.d $r0, [romfs_in_flash]
+
+ jump _start_it ; better skip the additional cramfs check below
+
+_start_it:
+
+ ;; the kernel stack is overlayed with the task structure for each
+ ;; task. thus the initial kernel stack is in the same page as the
+ ;; init_task (but starts in the top of the page, size 8192)
+ move.d init_thread_union + 8192, $sp
+ move.d ibr_start,$r0 ; this symbol is set by the linker script
+ move $r0,$ibr
+ move.d $r0,[etrax_irv] ; set the interrupt base register and pointer
+
+ ;; Clear 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
+
+#ifdef CONFIG_BLK_DEV_ETRAXIDE
+ ;; disable ATA before enabling it in genconfig below
+ moveq 0,$r0
+ move.d $r0,[R_ATA_CTRL_DATA]
+ move.d $r0,[R_ATA_TRANSFER_CNT]
+ move.d $r0,[R_ATA_CONFIG]
+#if 0
+ move.d R_PORT_G_DATA, $r1
+ move.d $r0, [$r1]; assert ATA bus-reset
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ move.d 0x08000000,$r0
+ move.d $r0,[$r1]
+#endif
+#endif
+
+#ifdef CONFIG_JULIETTE
+ ;; configure external DMA channel 0 before enabling it in genconfig
+
+ moveq 0,$r0
+ move.d $r0,[R_EXT_DMA_0_ADDR]
+ ; cnt enable, word size, output, stop, size 0
+ move.d IO_STATE (R_EXT_DMA_0_CMD, cnt, enable) \
+ | IO_STATE (R_EXT_DMA_0_CMD, rqpol, ahigh) \
+ | IO_STATE (R_EXT_DMA_0_CMD, apol, ahigh) \
+ | IO_STATE (R_EXT_DMA_0_CMD, rq_ack, burst) \
+ | IO_STATE (R_EXT_DMA_0_CMD, wid, word) \
+ | IO_STATE (R_EXT_DMA_0_CMD, dir, output) \
+ | IO_STATE (R_EXT_DMA_0_CMD, run, stop) \
+ | IO_FIELD (R_EXT_DMA_0_CMD, trf_count, 0),$r0
+ move.d $r0,[R_EXT_DMA_0_CMD]
+
+ ;; reset dma4 and wait for completion
+
+ moveq IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
+ move.b $r0,[R_DMA_CH4_CMD]
+1: move.b [R_DMA_CH4_CMD],$r0
+ and.b IO_MASK (R_DMA_CH4_CMD, cmd),$r0
+ cmp.b IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
+ beq 1b
+ nop
+
+ ;; reset dma5 and wait for completion
+
+ moveq IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
+ move.b $r0,[R_DMA_CH5_CMD]
+1: move.b [R_DMA_CH5_CMD],$r0
+ and.b IO_MASK (R_DMA_CH5_CMD, cmd),$r0
+ cmp.b IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
+ beq 1b
+ nop
+#endif
+
+ ;; Etrax product HW genconfig setup
+
+ moveq 0,$r0
+#if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \
+ && !defined(CONFIG_DMA_MEMCPY)
+ ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
+ or.d IO_STATE (R_GEN_CONFIG, dma7, serial0) \
+ | IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
+#endif
+#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1)
+ ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
+ or.d IO_STATE (R_GEN_CONFIG, dma9, serial1) \
+ | IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
+#endif
+#ifdef CONFIG_DMA_MEMCPY
+ ; 6/7 memory-memory DMA
+ or.d IO_STATE (R_GEN_CONFIG, dma7, intdma6) \
+ | IO_STATE (R_GEN_CONFIG, dma6, intdma7),$r0
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+ ; Enable serial port 2
+ or.w IO_STATE (R_GEN_CONFIG, ser2, select),$r0
+#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2)
+ ; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA
+ or.d IO_STATE (R_GEN_CONFIG, dma3, serial2) \
+ | IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
+#endif
+#endif
+#if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
+ ; Enable serial port 3
+ or.w IO_STATE (R_GEN_CONFIG, ser3, select),$r0
+#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3)
+ ; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA
+ or.d IO_STATE (R_GEN_CONFIG, dma5, serial3) \
+ | IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
+#endif
+#endif
+#if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
+ ; parport 0 enabled using DMA 2/3
+ or.w IO_STATE (R_GEN_CONFIG, par0, select),$r0
+#endif
+#if defined(CONFIG_ETRAX_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
+ ; parport 1 enabled using DMA 4/5
+ or.w IO_STATE (R_GEN_CONFIG, par1, select),$r0
+#endif
+#ifdef CONFIG_ETRAX_IDE
+ ; DMA channels 2 and 3 to ATA, ATA enabled
+ or.d IO_STATE (R_GEN_CONFIG, dma3, ata) \
+ | IO_STATE (R_GEN_CONFIG, dma2, ata) \
+ | IO_STATE (R_GEN_CONFIG, ata, select),$r0
+#endif
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+ ; Set the USB port 1 enable bit
+ or.d IO_STATE (R_GEN_CONFIG, usb1, select),$r0
+#endif
+#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+ ; Set the USB port 2 enable bit
+ or.d IO_STATE (R_GEN_CONFIG, usb2, select),$r0
+#endif
+#ifdef CONFIG_ETRAX_USB_HOST
+ ; Connect DMA channels 8 and 9 to USB
+ and.d (~(IO_MASK (R_GEN_CONFIG, dma9) \
+ | IO_MASK (R_GEN_CONFIG, dma8))) \
+ | IO_STATE (R_GEN_CONFIG, dma9, usb) \
+ | IO_STATE (R_GEN_CONFIG, dma8, usb),$r0
+#endif
+
+#ifdef CONFIG_JULIETTE
+ ; DMA channels 4 and 5 to EXTDMA0, for Juliette
+ or.d IO_STATE (R_GEN_CONFIG, dma5, extdma0) \
+ | IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0
+#endif
+
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
+ or.d IO_STATE (R_GEN_CONFIG, g0dir, out),$r0
+#endif
+
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT)
+ or.d IO_STATE (R_GEN_CONFIG, g8_15dir, out),$r0
+#endif
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT)
+ or.d IO_STATE (R_GEN_CONFIG, g16_23dir, out),$r0
+#endif
+
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT)
+ or.d IO_STATE (R_GEN_CONFIG, g24dir, out),$r0
+#endif
+
+ move.d $r0,[genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
+
+#ifndef CONFIG_SVINTO_SIM
+ move.d $r0,[R_GEN_CONFIG]
+
+#if 0
+ moveq 4,$r0
+ move.b $r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out)
+ move.b $r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in)
+1: move.b [R_DMA_CH6_CMD],$r0 ; wait for reset cycle to finish
+ and.b 7,$r0
+ cmp.b 4,$r0
+ beq 1b
+ nop
+1: move.b [R_DMA_CH7_CMD],$r0 ; wait for reset cycle to finish
+ and.b 7,$r0
+ cmp.b 4,$r0
+ beq 1b
+ nop
+#endif
+
+ moveq IO_STATE (R_DMA_CH8_CMD, cmd, reset),$r0
+ move.b $r0,[R_DMA_CH8_CMD] ; reset (ser1 dma out)
+ move.b $r0,[R_DMA_CH9_CMD] ; reset (ser1 dma in)
+1: move.b [R_DMA_CH8_CMD],$r0 ; wait for reset cycle to finish
+ andq IO_MASK (R_DMA_CH8_CMD, cmd),$r0
+ cmpq IO_STATE (R_DMA_CH8_CMD, cmd, reset),$r0
+ beq 1b
+ nop
+1: move.b [R_DMA_CH9_CMD],$r0 ; wait for reset cycle to finish
+ andq IO_MASK (R_DMA_CH9_CMD, cmd),$r0
+ cmpq IO_STATE (R_DMA_CH9_CMD, cmd, reset),$r0
+ beq 1b
+ nop
+
+ ;; setup port PA and PB default initial directions and data
+ ;; including their shadow registers
+
+ move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR,$r0
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
+ or.b IO_STATE (R_PORT_PA_DIR, dir7, output),$r0
+#endif
+ move.b $r0,[port_pa_dir_shadow]
+ move.b $r0,[R_PORT_PA_DIR]
+ move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA,$r0
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
+#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
+ and.b ~(1 << 7),$r0
+#else
+ or.b (1 << 7),$r0
+#endif
+#endif
+ move.b $r0,[port_pa_data_shadow]
+ move.b $r0,[R_PORT_PA_DATA]
+
+ move.b CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG,$r0
+ move.b $r0,[port_pb_config_shadow]
+ move.b $r0,[R_PORT_PB_CONFIG]
+ move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR,$r0
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
+ or.b IO_STATE (R_PORT_PB_DIR, dir5, output),$r0
+#endif
+ move.b $r0,[port_pb_dir_shadow]
+ move.b $r0,[R_PORT_PB_DIR]
+ move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA,$r0
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
+#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
+ and.b ~(1 << 5),$r0
+#else
+ or.b (1 << 5),$r0
+#endif
+#endif
+ move.b $r0,[port_pb_data_shadow]
+ move.b $r0,[R_PORT_PB_DATA]
+
+ moveq 0, $r0
+ move.d $r0,[port_pb_i2c_shadow]
+ move.d $r0, [R_PORT_PB_I2C]
+
+ moveq 0,$r0
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G10)
+#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
+ and.d ~(1 << 10),$r0
+#else
+ or.d (1 << 10),$r0
+#endif
+#endif
+#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G11)
+#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
+ and.d ~(1 << 11),$r0
+#else
+ or.d (1 << 11),$r0
+#endif
+#endif
+ move.d $r0,[port_g_data_shadow]
+ move.d $r0,[R_PORT_G_DATA]
+
+ ;; setup the serial port 0 at 115200 baud for debug purposes
+
+ moveq IO_STATE (R_SERIAL0_XOFF, tx_stop, enable) \
+ | IO_STATE (R_SERIAL0_XOFF, auto_xoff, disable) \
+ | IO_FIELD (R_SERIAL0_XOFF, xoff_char, 0),$r0
+ move.d $r0,[R_SERIAL0_XOFF]
+
+ ; 115.2kbaud for both transmit and receive
+ move.b IO_STATE (R_SERIAL0_BAUD, tr_baud, c115k2Hz) \
+ | IO_STATE (R_SERIAL0_BAUD, rec_baud, c115k2Hz),$r0
+ move.b $r0,[R_SERIAL0_BAUD]
+
+ ; Set up and enable the serial0 receiver.
+ move.b IO_STATE (R_SERIAL0_REC_CTRL, dma_err, stop) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rec_enable, enable) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rts_, active) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, sampling, middle) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rec_stick_par, normal) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rec_par, even) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rec_par_en, disable) \
+ | IO_STATE (R_SERIAL0_REC_CTRL, rec_bitnr, rec_8bit),$r0
+ move.b $r0,[R_SERIAL0_REC_CTRL]
+
+ ; Set up and enable the serial0 transmitter.
+ move.b IO_FIELD (R_SERIAL0_TR_CTRL, txd, 0) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, tr_enable, enable) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, auto_cts, disabled) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, stop_bits, one_bit) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, tr_stick_par, normal) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, tr_par, even) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, tr_par_en, disable) \
+ | IO_STATE (R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit),$r0
+ move.b $r0,[R_SERIAL0_TR_CTRL]
+
+ ;; setup the serial port 1 at 115200 baud for debug purposes
+
+ moveq IO_STATE (R_SERIAL1_XOFF, tx_stop, enable) \
+ | IO_STATE (R_SERIAL1_XOFF, auto_xoff, disable) \
+ | IO_FIELD (R_SERIAL1_XOFF, xoff_char, 0),$r0
+ move.d $r0,[R_SERIAL1_XOFF]
+
+ ; 115.2kbaud for both transmit and receive
+ move.b IO_STATE (R_SERIAL1_BAUD, tr_baud, c115k2Hz) \
+ | IO_STATE (R_SERIAL1_BAUD, rec_baud, c115k2Hz),$r0
+ move.b $r0,[R_SERIAL1_BAUD]
+
+ ; Set up and enable the serial1 receiver.
+ move.b IO_STATE (R_SERIAL1_REC_CTRL, dma_err, stop) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rec_enable, enable) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rts_, active) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, sampling, middle) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rec_stick_par, normal) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rec_par, even) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rec_par_en, disable) \
+ | IO_STATE (R_SERIAL1_REC_CTRL, rec_bitnr, rec_8bit),$r0
+ move.b $r0,[R_SERIAL1_REC_CTRL]
+
+ ; Set up and enable the serial1 transmitter.
+ move.b IO_FIELD (R_SERIAL1_TR_CTRL, txd, 0) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, tr_enable, enable) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, auto_cts, disabled) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, stop_bits, one_bit) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, tr_stick_par, normal) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, tr_par, even) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, tr_par_en, disable) \
+ | IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0
+ move.b $r0,[R_SERIAL1_TR_CTRL]
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+ ;; setup the serial port 3 at 115200 baud for debug purposes
+
+ moveq IO_STATE (R_SERIAL3_XOFF, tx_stop, enable) \
+ | IO_STATE (R_SERIAL3_XOFF, auto_xoff, disable) \
+ | IO_FIELD (R_SERIAL3_XOFF, xoff_char, 0),$r0
+ move.d $r0,[R_SERIAL3_XOFF]
+
+ ; 115.2kbaud for both transmit and receive
+ move.b IO_STATE (R_SERIAL3_BAUD, tr_baud, c115k2Hz) \
+ | IO_STATE (R_SERIAL3_BAUD, rec_baud, c115k2Hz),$r0
+ move.b $r0,[R_SERIAL3_BAUD]
+
+ ; Set up and enable the serial3 receiver.
+ move.b IO_STATE (R_SERIAL3_REC_CTRL, dma_err, stop) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rec_enable, enable) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rts_, active) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, sampling, middle) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rec_stick_par, normal) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rec_par, even) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rec_par_en, disable) \
+ | IO_STATE (R_SERIAL3_REC_CTRL, rec_bitnr, rec_8bit),$r0
+ move.b $r0,[R_SERIAL3_REC_CTRL]
+
+ ; Set up and enable the serial3 transmitter.
+ move.b IO_FIELD (R_SERIAL3_TR_CTRL, txd, 0) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, tr_enable, enable) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, auto_cts, disabled) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, stop_bits, one_bit) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, tr_stick_par, normal) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, tr_par, even) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, tr_par_en, disable) \
+ | IO_STATE (R_SERIAL3_TR_CTRL, tr_bitnr, tr_8bit),$r0
+ move.b $r0,[R_SERIAL3_TR_CTRL]
+#endif
+
+#endif /* CONFIG_SVINTO_SIM */
+
+ jump start_kernel ; jump into the C-function start_kernel in init/main.c
+
+ .data
+etrax_irv:
+ .dword 0
+romfs_start:
+ .dword 0
+romfs_length:
+ .dword 0
+romfs_in_flash:
+ .dword 0
+
+ ;; put some special pages at the beginning of the kernel aligned
+ ;; to page boundaries - the kernel cannot start until after this
+
+#ifdef CONFIG_CRIS_LOW_MAP
+swapper_pg_dir = 0x60002000
+#else
+swapper_pg_dir = 0xc0002000
+#endif
+
+ .section ".init.data", "aw"
+#include "../lib/hw_settings.S"
diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c
new file mode 100644
index 00000000000..b2f16d6fc87
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/irq.c
@@ -0,0 +1,204 @@
+/* $Id: irq.c,v 1.2 2004/06/09 05:30:27 starvik Exp $
+ *
+ * linux/arch/cris/kernel/irq.c
+ *
+ * Copyright (c) 2000-2002 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ * This file contains the interrupt vectors and some
+ * helper functions
+ *
+ */
+
+#include <asm/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
+
+/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
+ * global just so that the kernel gdb can use it.
+ */
+
+void
+set_int_vector(int n, irqvectptr addr)
+{
+ etrax_irv->v[n + 0x20] = (irqvectptr)addr;
+}
+
+/* the breakpoint vector is obviously not made just like the normal irq handlers
+ * but needs to contain _code_ to jump to addr.
+ *
+ * the BREAK n instruction jumps to IBR + n * 8
+ */
+
+void
+set_break_vector(int n, irqvectptr addr)
+{
+ unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2];
+ unsigned long *jaddr = (unsigned long *)(jinstr + 1);
+
+ /* if you don't know what this does, do not touch it! */
+
+ *jinstr = 0x0d3f;
+ *jaddr = (unsigned long)addr;
+
+ /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */
+}
+
+/*
+ * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ *
+ * These macros create the low-level assembly IRQ routines that do all
+ * the operations that are needed. They are also written to be fast - and to
+ * disable interrupts as little as humanly possible.
+ *
+ */
+
+/* IRQ0 and 1 are special traps */
+void hwbreakpoint(void);
+void IRQ1_interrupt(void);
+BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */
+BUILD_IRQ(3, 0x08)
+BUILD_IRQ(4, 0x10)
+BUILD_IRQ(5, 0x20)
+BUILD_IRQ(6, 0x40)
+BUILD_IRQ(7, 0x80)
+BUILD_IRQ(8, 0x100)
+BUILD_IRQ(9, 0x200)
+BUILD_IRQ(10, 0x400)
+BUILD_IRQ(11, 0x800)
+BUILD_IRQ(12, 0x1000)
+BUILD_IRQ(13, 0x2000)
+void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */
+void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */
+BUILD_IRQ(16, 0x10000)
+BUILD_IRQ(17, 0x20000)
+BUILD_IRQ(18, 0x40000)
+BUILD_IRQ(19, 0x80000)
+BUILD_IRQ(20, 0x100000)
+BUILD_IRQ(21, 0x200000)
+BUILD_IRQ(22, 0x400000)
+BUILD_IRQ(23, 0x800000)
+BUILD_IRQ(24, 0x1000000)
+BUILD_IRQ(25, 0x2000000)
+/* IRQ 26-30 are reserved */
+BUILD_IRQ(31, 0x80000000)
+
+/*
+ * Pointers to the low-level handlers
+ */
+
+static void (*interrupt[NR_IRQS])(void) = {
+ NULL, NULL, IRQ2_interrupt, IRQ3_interrupt,
+ IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
+ IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
+ IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,
+ IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
+ IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
+ IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
+ IRQ31_interrupt
+};
+
+static void (*bad_interrupt[NR_IRQS])(void) = {
+ NULL, NULL,
+ NULL, bad_IRQ3_interrupt,
+ bad_IRQ4_interrupt, bad_IRQ5_interrupt,
+ bad_IRQ6_interrupt, bad_IRQ7_interrupt,
+ bad_IRQ8_interrupt, bad_IRQ9_interrupt,
+ bad_IRQ10_interrupt, bad_IRQ11_interrupt,
+ bad_IRQ12_interrupt, bad_IRQ13_interrupt,
+ NULL, NULL,
+ bad_IRQ16_interrupt, bad_IRQ17_interrupt,
+ bad_IRQ18_interrupt, bad_IRQ19_interrupt,
+ bad_IRQ20_interrupt, bad_IRQ21_interrupt,
+ bad_IRQ22_interrupt, bad_IRQ23_interrupt,
+ bad_IRQ24_interrupt, bad_IRQ25_interrupt,
+ NULL, NULL, NULL, NULL, NULL,
+ bad_IRQ31_interrupt
+};
+
+void arch_setup_irq(int irq)
+{
+ set_int_vector(irq, interrupt[irq]);
+}
+
+void arch_free_irq(int irq)
+{
+ set_int_vector(irq, bad_interrupt[irq]);
+}
+
+void weird_irq(void);
+void system_call(void); /* from entry.S */
+void do_sigtrap(void); /* from entry.S */
+void gdb_handle_breakpoint(void); /* from entry.S */
+
+/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
+ setting the irq vector table to point to bad_interrupt ptrs.
+*/
+
+void __init
+init_IRQ(void)
+{
+ int i;
+
+ /* clear all interrupt masks */
+
+#ifndef CONFIG_SVINTO_SIM
+ *R_IRQ_MASK0_CLR = 0xffffffff;
+ *R_IRQ_MASK1_CLR = 0xffffffff;
+ *R_IRQ_MASK2_CLR = 0xffffffff;
+#endif
+
+ *R_VECT_MASK_CLR = 0xffffffff;
+
+ /* clear the shortcut entry points */
+
+ for(i = 0; i < NR_IRQS; i++)
+ irq_shortcuts[i] = NULL;
+
+ for (i = 0; i < 256; i++)
+ etrax_irv->v[i] = weird_irq;
+
+ /* the entries in the break vector contain actual code to be
+ executed by the associated break handler, rather than just a jump
+ address. therefore we need to setup a default breakpoint handler
+ for all breakpoints */
+
+ for (i = 0; i < 16; i++)
+ set_break_vector(i, do_sigtrap);
+
+ /* set all etrax irq's to the bad handlers */
+ for (i = 2; i < NR_IRQS; i++)
+ set_int_vector(i, bad_interrupt[i]);
+
+ /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
+
+ set_int_vector(15, multiple_interrupt);
+
+ /* 0 and 1 which are special breakpoint/NMI traps */
+
+ set_int_vector(0, hwbreakpoint);
+ set_int_vector(1, IRQ1_interrupt);
+
+ /* and irq 14 which is the mmu bus fault handler */
+
+ set_int_vector(14, mmu_bus_fault);
+
+ /* setup the system-call trap, which is reached by BREAK 13 */
+
+ set_break_vector(13, system_call);
+
+ /* setup a breakpoint handler for debugging used for both user and
+ kernel mode debugging (which is why it is not inside an ifdef
+ CONFIG_ETRAX_KGDB) */
+ set_break_vector(8, gdb_handle_breakpoint);
+
+#ifdef CONFIG_ETRAX_KGDB
+ /* setup kgdb if its enabled, and break into the debugger */
+ kgdb_init();
+ breakpoint();
+#endif
+}
diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c
new file mode 100644
index 00000000000..7d368c877ee
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/kgdb.c
@@ -0,0 +1,1568 @@
+/*!**************************************************************************
+*!
+*! FILE NAME : kgdb.c
+*!
+*! DESCRIPTION: Implementation of the gdb stub with respect to ETRAX 100.
+*! It is a mix of arch/m68k/kernel/kgdb.c and cris_stub.c.
+*!
+*!---------------------------------------------------------------------------
+*! HISTORY
+*!
+*! DATE NAME CHANGES
+*! ---- ---- -------
+*! Apr 26 1999 Hendrik Ruijter Initial version.
+*! May 6 1999 Hendrik Ruijter Removed call to strlen in libc and removed
+*! struct assignment as it generates calls to
+*! memcpy in libc.
+*! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'.
+*! Jul 21 1999 Bjorn Wesen eLinux port
+*!
+*! $Log: kgdb.c,v $
+*! Revision 1.5 2004/10/07 13:59:08 starvik
+*! Corrected call to set_int_vector
+*!
+*! Revision 1.4 2003/04/09 05:20:44 starvik
+*! Merge of Linux 2.5.67
+*!
+*! Revision 1.3 2003/01/21 19:11:08 starvik
+*! Modified include path for new dir layout
+*!
+*! Revision 1.2 2002/11/19 14:35:24 starvik
+*! Changes from linux 2.4
+*! Changed struct initializer syntax to the currently prefered notation
+*!
+*! Revision 1.1 2001/12/17 13:59:27 bjornw
+*! Initial revision
+*!
+*! Revision 1.6 2001/10/09 13:10:03 matsfg
+*! Added $ on registers and removed some underscores
+*!
+*! Revision 1.5 2001/04/17 13:58:39 orjanf
+*! * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB.
+*!
+*! Revision 1.4 2001/02/23 13:45:19 bjornw
+*! config.h check
+*!
+*! Revision 1.3 2001/01/31 18:08:23 orjanf
+*! Removed kgdb_handle_breakpoint from being the break 8 handler.
+*!
+*! Revision 1.2 2001/01/12 14:22:25 orjanf
+*! Updated kernel debugging support to work with ETRAX 100LX.
+*!
+*! Revision 1.1 2000/07/10 16:25:21 bjornw
+*! Initial revision
+*!
+*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw
+*! * Initial version of arch/cris, the latest CRIS architecture with an MMU.
+*! Mostly copied from arch/etrax100 with appropriate renames of files.
+*! The mm/ subdir is copied from arch/i386.
+*! This does not compile yet at all.
+*!
+*!
+*! Revision 1.4 1999/07/22 17:25:25 bjornw
+*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors.
+*!
+*! Revision 1.3 1999/07/21 19:51:18 bjornw
+*! Check if the interrupting char is a ctrl-C, ignore otherwise.
+*!
+*! Revision 1.2 1999/07/21 18:09:39 bjornw
+*! Ported to eLinux architecture, and added some kgdb documentation.
+*!
+*!
+*!---------------------------------------------------------------------------
+*!
+*! $Id: kgdb.c,v 1.5 2004/10/07 13:59:08 starvik Exp $
+*!
+*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
+*!
+*!**************************************************************************/
+/* @(#) cris_stub.c 1.3 06/17/99 */
+
+/*
+ * 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 <asm/setup.h>
+#include <asm/ptrace.h>
+
+#include <asm/arch/svinto.h>
+#include <asm/irq.h>
+
+static int kgdb_started = 0;
+
+/********************************* Register image ****************************/
+/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's
+ Reference", p. 1-1, with the additional register definitions of the
+ ETRAX 100LX in cris-opc.h.
+ There are 16 general 32-bit registers, R0-R15, where R14 is the stack
+ pointer, SP, and R15 is the program counter, PC.
+ There are 16 special registers, P0-P15, where three of the unimplemented
+ registers, P0, P4 and P8, are reserved as zero-registers. A read from
+ any of these registers returns zero and a write has no effect. */
+
+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 */
+ unsigned int r9; /* 0x24 */
+ unsigned int r10; /* 0x28 */
+ unsigned int r11; /* 0x2C */
+ unsigned int r12; /* 0x30 */
+ unsigned int r13; /* 0x34 */
+ unsigned int sp; /* 0x38 Stack pointer */
+ unsigned int pc; /* 0x3C Program counter */
+
+ unsigned char p0; /* 0x40 8-bit zero-register */
+ unsigned char vr; /* 0x41 Version register */
+
+ unsigned short p4; /* 0x42 16-bit zero-register */
+ unsigned short ccr; /* 0x44 Condition code register */
+
+ unsigned int mof; /* 0x46 Multiply overflow register */
+
+ unsigned int p8; /* 0x4A 32-bit zero-register */
+ unsigned int ibr; /* 0x4E Interrupt base register */
+ unsigned int irp; /* 0x52 Interrupt return pointer */
+ unsigned int srp; /* 0x56 Subroutine return pointer */
+ unsigned int bar; /* 0x5A Breakpoint address register */
+ unsigned int dccr; /* 0x5E Double condition code register */
+ unsigned int brp; /* 0x62 Breakpoint return pointer (pc in caller) */
+ unsigned int usp; /* 0x66 User mode stack pointer */
+} registers;
+
+/************** 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. **********************/
+/* Copy the content of a register image into another. The size n is
+ the size of the register image. Due to struct assignment generation of
+ memcpy in libc. */
+static void copy_registers (registers *dptr, registers *sptr, int n);
+
+/* Copy the stored registers from the stack. Put the register contents
+ of thread thread_id in the struct reg. */
+static void copy_registers_from_stack (int thread_id, registers *reg);
+
+/* Copy the registers to the stack. Put the register contents of thread
+ thread_id from struct reg to the stack. */
+static void copy_registers_to_stack (int thread_id, registers *reg);
+
+/* Write a value to a specified register regno in the register image
+ of the current thread. */
+static int write_register (int regno, char *val);
+
+/* Write a value to a specified register in the stack of a thread other
+ than the current thread. */
+static write_stack_register (int thread_id, int regno, char *valptr);
+
+/* 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);
+
+void enableDebugIRQ (void);
+
+/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte,
+ represented by int x. */
+static char highhex (int x);
+
+/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte,
+ represented by int x. */
+static char lowhex (int x);
+
+/* 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. */
+static 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 length); /* used by etrax100ser.c */
+
+/* The hook for both static (compiled) and dynamic breakpoints set by GDB.
+ ETRAX 100 specific. */
+void handle_breakpoint (void); /* used by irq.c */
+
+/* The hook for an interrupt generated by GDB. ETRAX 100 specific. */
+void handle_interrupt (void); /* used by irq.c */
+
+/* A static breakpoint to be used at startup. */
+void breakpoint (void); /* called by init/main.c */
+
+/* From osys_int.c, executing_task contains the number of the current
+ executing task in osys. Does not know of object-oriented threads. */
+extern unsigned char executing_task;
+
+/* The number of characters used for a 64 bit thread identifier. */
+#define HEXCHARS_IN_THREAD_ID 16
+
+/* 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 */
+#define BUFMAX 512
+
+/* Run-length encoding maximum length. Send 64 at most. */
+#define RUNLENMAX 64
+
+/* Definition of all valid hexadecimal characters */
+static const char hexchars[] = "0123456789abcdef";
+
+/* The inbound/outbound buffers used in packet I/O */
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+/* Error and warning messages. */
+enum error_type
+{
+ SUCCESS, E01, E02, E03, E04, E05, E06, E07
+};
+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.",
+ "E07 Change register content - P - the register is not stored on the stack"
+};
+/********************************* Register image ****************************/
+/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's
+ Reference", p. 1-1, with the additional register definitions of the
+ ETRAX 100LX in cris-opc.h.
+ There are 16 general 32-bit registers, R0-R15, where R14 is the stack
+ pointer, SP, and R15 is the program counter, PC.
+ There are 16 special registers, P0-P15, where three of the unimplemented
+ registers, P0, P4 and P8, are reserved as zero-registers. A read from
+ any of these registers returns zero and a write has no effect. */
+enum register_name
+{
+ R0, R1, R2, R3,
+ R4, R5, R6, R7,
+ R8, R9, R10, R11,
+ R12, R13, SP, PC,
+ P0, VR, P2, P3,
+ P4, CCR, P6, MOF,
+ P8, IBR, IRP, SRP,
+ BAR, DCCR, BRP, USP
+};
+
+/* 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, 0, 0,
+ 2, 2, 0, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4
+};
+
+/* Contains the register image of the executing thread in the assembler
+ part of the code in order to avoid horrible addressing modes. */
+static registers reg;
+
+/* FIXME: Should this be used? Delete otherwise. */
+/* Contains the assumed consistency state of the register image. Uses the
+ enum error_type for state information. */
+static int consistency_status = SUCCESS;
+
+/********************************** Handle exceptions ************************/
+/* The variable reg contains the register image associated with the
+ current_thread_c variable. It is a complete register image created at
+ entry. The reg_g contains a register image of a task where the general
+ registers are taken from the stack and all special registers are taken
+ from the executing task. It is associated with current_thread_g and used
+ in order to provide access mainly for 'g', 'G' and 'P'.
+*/
+
+/* Need two task id pointers in order to handle Hct and Hgt commands. */
+static int current_thread_c = 0;
+static int current_thread_g = 0;
+
+/* Need two register images in order to handle Hct and Hgt commands. The
+ variable reg_g is in addition to reg above. */
+static registers reg_g;
+
+/********************************** Breakpoint *******************************/
+/* Use an internal stack in the breakpoint and interrupt response routines */
+#define INTERNAL_STACK_SIZE 1024
+static 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
+ BRP as it is whereas a dynamic breakpoint requires subtraction with 2
+ in order to execute the instruction. The first breakpoint is static. */
+static unsigned char is_dyn_brkp = 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(hexchars, *s1, base)) != NULL; ++s1)
+ x = x * base + (sd - hexchars);
+
+ if (endptr)
+ {
+ /* Unconverted suffix is stored in endptr unless endptr is NULL. */
+ *endptr = s1;
+ }
+
+ return x;
+}
+
+int
+double_this(int x)
+{
+ return 2 * x;
+}
+
+/********************************* Register image ****************************/
+/* Copy the content of a register image into another. The size n is
+ the size of the register image. Due to struct assignment generation of
+ memcpy in libc. */
+static void
+copy_registers (registers *dptr, registers *sptr, int n)
+{
+ unsigned char *dreg;
+ unsigned char *sreg;
+
+ for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--)
+ *dreg++ = *sreg++;
+}
+
+#ifdef PROCESS_SUPPORT
+/* Copy the stored registers from the stack. Put the register contents
+ of thread thread_id in the struct reg. */
+static void
+copy_registers_from_stack (int thread_id, registers *regptr)
+{
+ int j;
+ stack_registers *s = (stack_registers *)stack_list[thread_id];
+ unsigned int *d = (unsigned int *)regptr;
+
+ for (j = 13; j >= 0; j--)
+ *d++ = s->r[j];
+ regptr->sp = (unsigned int)stack_list[thread_id];
+ regptr->pc = s->pc;
+ regptr->dccr = s->dccr;
+ regptr->srp = s->srp;
+}
+
+/* Copy the registers to the stack. Put the register contents of thread
+ thread_id from struct reg to the stack. */
+static void
+copy_registers_to_stack (int thread_id, registers *regptr)
+{
+ int i;
+ stack_registers *d = (stack_registers *)stack_list[thread_id];
+ unsigned int *s = (unsigned int *)regptr;
+
+ for (i = 0; i < 14; i++) {
+ d->r[i] = *s++;
+ }
+ d->pc = regptr->pc;
+ d->dccr = regptr->dccr;
+ d->srp = regptr->srp;
+}
+#endif
+
+/* 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;
+ registers *current_reg = &reg;
+
+ if (regno >= R0 && regno <= PC) {
+ /* 32-bit register with simple offset. */
+ hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+ }
+ else if (regno == P0 || regno == VR || regno == P4 || regno == P8) {
+ /* Do not support read-only registers. */
+ status = E02;
+ }
+ else if (regno == CCR) {
+ /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented,
+ and P7 (MOF) is 32 bits in ETRAX 100LX. */
+ hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short),
+ val, sizeof(unsigned short));
+ }
+ else if (regno >= MOF && regno <= USP) {
+ /* 32 bit register with complex offset. (P8 has been taken care of.) */
+ hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int),
+ val, sizeof(unsigned int));
+ }
+ else {
+ /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */
+ status = E05;
+ }
+ return status;
+}
+
+#ifdef PROCESS_SUPPORT
+/* Write a value to a specified register in the stack of a thread other
+ than the current thread. Returns status code SUCCESS or E07. */
+static int
+write_stack_register (int thread_id, int regno, char *valptr)
+{
+ int status = SUCCESS;
+ stack_registers *d = (stack_registers *)stack_list[thread_id];
+ unsigned int val;
+
+ hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int));
+ if (regno >= R0 && regno < SP) {
+ d->r[regno] = val;
+ }
+ else if (regno == SP) {
+ stack_list[thread_id] = val;
+ }
+ else if (regno == PC) {
+ d->pc = val;
+ }
+ else if (regno == SRP) {
+ d->srp = val;
+ }
+ else if (regno == DCCR) {
+ d->dccr = val;
+ }
+ else {
+ /* Do not support registers in the current thread. */
+ status = E07;
+ }
+ return status;
+}
+#endif
+
+/* Read a value from a specified register in the register image. Returns the
+ value in the register or -1 for non-implemented registers.
+ Should check consistency_status after a call which may be E05 after changes
+ in the implementation. */
+static int
+read_register (char regno, unsigned int *valptr)
+{
+ registers *current_reg = &reg;
+
+ if (regno >= R0 && regno <= PC) {
+ /* 32-bit register with simple offset. */
+ *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int));
+ return SUCCESS;
+ }
+ else if (regno == P0 || regno == VR) {
+ /* 8 bit register with complex offset. */
+ *valptr = (unsigned int)(*(unsigned char *)
+ ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char)));
+ return SUCCESS;
+ }
+ else if (regno == P4 || regno == CCR) {
+ /* 16 bit register with complex offset. */
+ *valptr = (unsigned int)(*(unsigned short *)
+ ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short)));
+ return SUCCESS;
+ }
+ else if (regno >= MOF && regno <= USP) {
+ /* 32 bit register with complex offset. */
+ *valptr = *(unsigned int *)((char *)&(current_reg->p8)
+ + (regno-P8) * sizeof(unsigned int));
+ return SUCCESS;
+ }
+ else {
+ /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */
+ consistency_status = E05;
+ return E05;
+ }
+}
+
+/********************************** Packet I/O ******************************/
+/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte,
+ represented by int x. */
+static inline char
+highhex(int x)
+{
+ return hexchars[(x >> 4) & 0xf];
+}
+
+/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte,
+ represented by int x. */
+static inline char
+lowhex(int x)
+{
+ return hexchars[x & 0xf];
+}
+
+/* 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 int do_printk = 0;
+
+static char *
+mem2hex(char *buf, unsigned char *mem, int count)
+{
+ int i;
+ int ch;
+
+ if (mem == NULL) {
+ /* Bogus read from m0. FIXME: What constitutes a valid address? */
+ for (i = 0; i < count; i++) {
+ *buf++ = '0';
+ *buf++ = '0';
+ }
+ } else {
+ /* Valid mem address. */
+ for (i = 0; i < count; i++) {
+ ch = *mem++;
+ *buf++ = highhex (ch);
+ *buf++ = lowhex (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;
+ }
+ 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 (highhex (checksum));
+ putDebugChar (lowhex (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 length)
+{
+ remcomOutBuffer[0] = 'O';
+ mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length);
+ putpacket(remcomOutBuffer);
+}
+
+/********************************** 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 = remcomOutBuffer;
+ int regno;
+
+ unsigned int reg_cont;
+ int status;
+
+ /* Send trap type (converted to signal) */
+
+ *ptr++ = 'T';
+ *ptr++ = highhex (sigval);
+ *ptr++ = lowhex (sigval);
+
+ /* Send register contents. We probably only need to send the
+ * PC, frame pointer and stack pointer here. Other registers will be
+ * explicitely asked for. But for now, send all.
+ */
+
+ for (regno = R0; regno <= USP; regno++) {
+ /* Store n...:r...; for the registers in the buffer. */
+
+ status = read_register (regno, &reg_cont);
+
+ if (status == SUCCESS) {
+
+ *ptr++ = highhex (regno);
+ *ptr++ = lowhex (regno);
+ *ptr++ = ':';
+
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont,
+ register_size[regno]);
+ *ptr++ = ';';
+ }
+
+ }
+
+#ifdef PROCESS_SUPPORT
+ /* Store the registers of the executing thread. Assume that both step,
+ continue, and register content requests are with respect to this
+ thread. The executing task is from the operating system scheduler. */
+
+ current_thread_c = executing_task;
+ current_thread_g = executing_task;
+
+ /* A struct assignment translates into a libc memcpy call. Avoid
+ all libc functions in order to prevent recursive break points. */
+ copy_registers (&reg_g, &reg, sizeof(registers));
+
+ /* Store thread:r...; with the executing task TID. */
+ gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:");
+ pos += gdb_cris_strlen ("thread:");
+ remcomOutBuffer[pos++] = highhex (executing_task);
+ remcomOutBuffer[pos++] = lowhex (executing_task);
+ gdb_cris_strcpy (&remcomOutBuffer[pos], ";");
+#endif
+
+ /* null-terminate and send it off */
+
+ *ptr = 0;
+
+ putpacket (remcomOutBuffer);
+}
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. */
+static void
+handle_exception (int sigval)
+{
+ /* Avoid warning of not used. */
+
+ USEDFUN(handle_exception);
+ USEDVAR(internal_stack[0]);
+
+ /* Send response. */
+
+ stub_is_stopped (sigval);
+
+ for (;;) {
+ remcomOutBuffer[0] = '\0';
+ getpacket (remcomInBuffer);
+ switch (remcomInBuffer[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. */
+
+ {
+#ifdef PROCESS_SUPPORT
+ /* Use the special register content in the executing thread. */
+ copy_registers (&reg_g, &reg, sizeof(registers));
+ /* Replace the content available on the stack. */
+ if (current_thread_g != executing_task) {
+ copy_registers_from_stack (current_thread_g, &reg_g);
+ }
+ mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)&reg_g, sizeof(registers));
+#else
+ mem2hex(remcomOutBuffer, (char *)&reg, sizeof(registers));
+#endif
+ }
+ break;
+
+ case 'G':
+ /* Write registers. GXX..XX
+ Each byte of register data is described by two hex digits.
+ Success: OK
+ Failure: void. */
+#ifdef PROCESS_SUPPORT
+ hex2mem ((unsigned char *)&reg_g, &remcomInBuffer[1], sizeof(registers));
+ if (current_thread_g == executing_task) {
+ copy_registers (&reg, &reg_g, sizeof(registers));
+ }
+ else {
+ copy_registers_to_stack(current_thread_g, &reg_g);
+ }
+#else
+ hex2mem((char *)&reg, &remcomInBuffer[1], sizeof(registers));
+#endif
+ gdb_cris_strcpy (remcomOutBuffer, "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 (&remcomInBuffer[1], &suffix, 16);
+ int status;
+#ifdef PROCESS_SUPPORT
+ if (current_thread_g != executing_task)
+ status = write_stack_register (current_thread_g, regno, suffix+1);
+ else
+#endif
+ status = write_register (regno, suffix+1);
+
+ switch (status) {
+ case E02:
+ /* Do not support read-only registers. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E02]);
+ break;
+ case E05:
+ /* Do not support non-existing registers. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E05]);
+ break;
+ case E07:
+ /* Do not support non-existing registers on the stack. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E07]);
+ break;
+ default:
+ /* Valid register number. */
+ gdb_cris_strcpy (remcomOutBuffer, "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(&remcomInBuffer[1],
+ &suffix, 16); int length = gdb_cris_strtol(suffix+1, 0, 16);
+
+ mem2hex(remcomOutBuffer, addr, length);
+ }
+ 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(&remcomInBuffer[1],
+ &lenptr, 16);
+ int length = gdb_cris_strtol(lenptr+1, &dataptr, 16);
+ if (*lenptr == ',' && *dataptr == ':') {
+ if (remcomInBuffer[0] == 'M') {
+ hex2mem(addr, dataptr + 1, length);
+ }
+ else /* X */ {
+ bin2mem(addr, dataptr + 1, length);
+ }
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else {
+ gdb_cris_strcpy (remcomOutBuffer, 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 (remcomInBuffer[1] != '\0') {
+ reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16);
+ }
+ enableDebugIRQ();
+ 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.
+
+ Should never be invoked. The single-step is implemented on
+ the host side. If ever invoked, it is an internal error E04. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E04]);
+ putpacket (remcomOutBuffer);
+ return;
+
+ case '?':
+ /* The last signal which caused a stop. ?
+ Success: SAA, where AA is the signal number.
+ Failure: void. */
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = highhex (sigval);
+ remcomOutBuffer[2] = lowhex (sigval);
+ remcomOutBuffer[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 */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E04]);
+ break;
+#ifdef PROCESS_SUPPORT
+
+ case 'T':
+ /* Thread alive. TXX
+ Is thread XX alive?
+ Success: OK, thread XX is alive.
+ Failure: E03, thread XX is dead. */
+ {
+ int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16);
+ /* Cannot tell whether it is alive or not. */
+ if (thread_id >= 0 && thread_id < number_of_tasks)
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ break;
+
+ case 'H':
+ /* Set thread for subsequent operations: Hct
+ c = 'c' for thread used in step and continue;
+ t can be -1 for all threads.
+ c = 'g' for thread used in other operations.
+ t = 0 means pick any thread.
+ Success: OK
+ Failure: E01 */
+ {
+ int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16);
+ if (remcomInBuffer[1] == 'c') {
+ /* c = 'c' for thread used in step and continue */
+ /* Do not change current_thread_c here. It would create a mess in
+ the scheduler. */
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else if (remcomInBuffer[1] == 'g') {
+ /* c = 'g' for thread used in other operations.
+ t = 0 means pick any thread. Impossible since the scheduler does
+ not allow that. */
+ if (thread_id >= 0 && thread_id < number_of_tasks) {
+ current_thread_g = thread_id;
+ gdb_cris_strcpy (remcomOutBuffer, "OK");
+ }
+ else {
+ /* Not expected - send an error message. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E01]);
+ }
+ }
+ else {
+ /* Not expected - send an error message. */
+ gdb_cris_strcpy (remcomOutBuffer, error_message[E01]);
+ }
+ }
+ break;
+
+ case 'q':
+ case 'Q':
+ /* Query of general interest. qXXXX
+ Set general value XXXX. QXXXX=yyyy */
+ {
+ int pos;
+ int nextpos;
+ int thread_id;
+
+ switch (remcomInBuffer[1]) {
+ case 'C':
+ /* Identify the remote current thread. */
+ gdb_cris_strcpy (&remcomOutBuffer[0], "QC");
+ remcomOutBuffer[2] = highhex (current_thread_c);
+ remcomOutBuffer[3] = lowhex (current_thread_c);
+ remcomOutBuffer[4] = '\0';
+ break;
+ case 'L':
+ gdb_cris_strcpy (&remcomOutBuffer[0], "QM");
+ /* Reply with number of threads. */
+ if (os_is_started()) {
+ remcomOutBuffer[2] = highhex (number_of_tasks);
+ remcomOutBuffer[3] = lowhex (number_of_tasks);
+ }
+ else {
+ remcomOutBuffer[2] = highhex (0);
+ remcomOutBuffer[3] = lowhex (1);
+ }
+ /* Done with the reply. */
+ remcomOutBuffer[4] = lowhex (1);
+ pos = 5;
+ /* Expects the argument thread id. */
+ for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++)
+ remcomOutBuffer[pos] = remcomInBuffer[pos];
+ /* Reply with the thread identifiers. */
+ if (os_is_started()) {
+ /* Store the thread identifiers of all tasks. */
+ for (thread_id = 0; thread_id < number_of_tasks; thread_id++) {
+ nextpos = pos + HEXCHARS_IN_THREAD_ID - 1;
+ for (; pos < nextpos; pos ++)
+ remcomOutBuffer[pos] = lowhex (0);
+ remcomOutBuffer[pos++] = lowhex (thread_id);
+ }
+ }
+ else {
+ /* Store the thread identifier of the boot task. */
+ nextpos = pos + HEXCHARS_IN_THREAD_ID - 1;
+ for (; pos < nextpos; pos ++)
+ remcomOutBuffer[pos] = lowhex (0);
+ remcomOutBuffer[pos++] = lowhex (current_thread_c);
+ }
+ remcomOutBuffer[pos] = '\0';
+ break;
+ default:
+ /* Not supported: "" */
+ /* Request information about section offsets: qOffsets. */
+ remcomOutBuffer[0] = 0;
+ break;
+ }
+ }
+ break;
+#endif /* PROCESS_SUPPORT */
+
+ 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. */
+ remcomOutBuffer[0] = 0;
+ break;
+ }
+ putpacket(remcomOutBuffer);
+ }
+}
+
+/* The jump is to the address 0x00000002. Performs a complete re-start
+ from scratch. */
+static void
+kill_restart ()
+{
+ __asm__ volatile ("jump 2");
+}
+
+/********************************** Breakpoint *******************************/
+/* The hook for both a static (compiled) and a dynamic breakpoint set by GDB.
+ An internal stack is used by the stub. The register image of the caller is
+ stored in the structure register_image.
+ Interactive communication with the host is handled by handle_exception and
+ finally the register image is restored. */
+
+void kgdb_handle_breakpoint(void);
+
+asm ("
+ .global kgdb_handle_breakpoint
+kgdb_handle_breakpoint:
+;;
+;; Response to the break-instruction
+;;
+;; Create a register image of the caller
+;;
+ move $dccr,[reg+0x5E] ; Save the flags in DCCR before disable interrupts
+ di ; Disable interrupts
+ move.d $r0,[reg] ; Save R0
+ move.d $r1,[reg+0x04] ; Save R1
+ move.d $r2,[reg+0x08] ; Save R2
+ move.d $r3,[reg+0x0C] ; Save R3
+ move.d $r4,[reg+0x10] ; Save R4
+ move.d $r5,[reg+0x14] ; Save R5
+ move.d $r6,[reg+0x18] ; Save R6
+ move.d $r7,[reg+0x1C] ; Save R7
+ move.d $r8,[reg+0x20] ; Save R8
+ move.d $r9,[reg+0x24] ; Save R9
+ move.d $r10,[reg+0x28] ; Save R10
+ move.d $r11,[reg+0x2C] ; Save R11
+ move.d $r12,[reg+0x30] ; Save R12
+ move.d $r13,[reg+0x34] ; Save R13
+ move.d $sp,[reg+0x38] ; Save SP (R14)
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,$r0
+ subq 2,$r0 ; Set to address of previous instruction.
+ move.d $r0,[reg+0x3c] ; Save the address in PC (R15)
+ clear.b [reg+0x40] ; Clear P0
+ move $vr,[reg+0x41] ; Save special register P1
+ clear.w [reg+0x42] ; Clear P4
+ move $ccr,[reg+0x44] ; Save special register CCR
+ move $mof,[reg+0x46] ; P7
+ clear.d [reg+0x4A] ; Clear P8
+ move $ibr,[reg+0x4E] ; P9,
+ move $irp,[reg+0x52] ; P10,
+ move $srp,[reg+0x56] ; P11,
+ move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR
+ ; P13, register DCCR already saved
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,r0
+;; Static (compiled) breakpoints must return to the next instruction in order
+;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction
+;; in order to execute it when execution is continued.
+ test.b [is_dyn_brkp] ; Is this a dynamic breakpoint?
+ beq is_static ; No, a static breakpoint
+ nop
+ subq 2,$r0 ; rerun the instruction the break replaced
+is_static:
+ moveq 1,$r1
+ move.b $r1,[is_dyn_brkp] ; Set the state variable to dynamic breakpoint
+ move.d $r0,[reg+0x62] ; Save the return address in BRP
+ move $usp,[reg+0x66] ; USP
+;;
+;; Handle the communication
+;;
+ move.d internal_stack+1020,$sp ; Use the internal stack which grows upward
+ moveq 5,$r10 ; SIGTRAP
+ jsr handle_exception ; Interactive routine
+;;
+;; Return to the caller
+;;
+ move.d [reg],$r0 ; Restore R0
+ move.d [reg+0x04],$r1 ; Restore R1
+ move.d [reg+0x08],$r2 ; Restore R2
+ move.d [reg+0x0C],$r3 ; Restore R3
+ move.d [reg+0x10],$r4 ; Restore R4
+ move.d [reg+0x14],$r5 ; Restore R5
+ move.d [reg+0x18],$r6 ; Restore R6
+ move.d [reg+0x1C],$r7 ; Restore R7
+ move.d [reg+0x20],$r8 ; Restore R8
+ move.d [reg+0x24],$r9 ; Restore R9
+ move.d [reg+0x28],$r10 ; Restore R10
+ move.d [reg+0x2C],$r11 ; Restore R11
+ move.d [reg+0x30],$r12 ; Restore R12
+ move.d [reg+0x34],$r13 ; Restore R13
+;;
+;; FIXME: Which registers should be restored?
+;;
+ move.d [reg+0x38],$sp ; Restore SP (R14)
+ move [reg+0x56],$srp ; Restore the subroutine return pointer.
+ move [reg+0x5E],$dccr ; Restore DCCR
+ move [reg+0x66],$usp ; Restore USP
+ jump [reg+0x62] ; A jump to the content in register BRP works.
+ nop ;
+");
+
+/* The hook for an interrupt generated by GDB. An internal stack is used
+ by the stub. The register image of the caller is stored in the structure
+ register_image. Interactive communication with the host is handled by
+ handle_exception and finally the register image is restored. Due to the
+ old assembler which does not recognise the break instruction and the
+ breakpoint return pointer hex-code is used. */
+
+void kgdb_handle_serial(void);
+
+asm ("
+ .global kgdb_handle_serial
+kgdb_handle_serial:
+;;
+;; Response to a serial interrupt
+;;
+
+ move $dccr,[reg+0x5E] ; Save the flags in DCCR
+ di ; Disable interrupts
+ move.d $r0,[reg] ; Save R0
+ move.d $r1,[reg+0x04] ; Save R1
+ move.d $r2,[reg+0x08] ; Save R2
+ move.d $r3,[reg+0x0C] ; Save R3
+ move.d $r4,[reg+0x10] ; Save R4
+ move.d $r5,[reg+0x14] ; Save R5
+ move.d $r6,[reg+0x18] ; Save R6
+ move.d $r7,[reg+0x1C] ; Save R7
+ move.d $r8,[reg+0x20] ; Save R8
+ move.d $r9,[reg+0x24] ; Save R9
+ move.d $r10,[reg+0x28] ; Save R10
+ move.d $r11,[reg+0x2C] ; Save R11
+ move.d $r12,[reg+0x30] ; Save R12
+ move.d $r13,[reg+0x34] ; Save R13
+ move.d $sp,[reg+0x38] ; Save SP (R14)
+ move $irp,[reg+0x3c] ; Save the address in PC (R15)
+ clear.b [reg+0x40] ; Clear P0
+ move $vr,[reg+0x41] ; Save special register P1,
+ clear.w [reg+0x42] ; Clear P4
+ move $ccr,[reg+0x44] ; Save special register CCR
+ move $mof,[reg+0x46] ; P7
+ clear.d [reg+0x4A] ; Clear P8
+ move $ibr,[reg+0x4E] ; P9,
+ move $irp,[reg+0x52] ; P10,
+ move $srp,[reg+0x56] ; P11,
+ move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR
+ ; P13, register DCCR already saved
+;; Due to the old assembler-versions BRP might not be recognized
+ .word 0xE670 ; move brp,r0
+ move.d $r0,[reg+0x62] ; Save the return address in BRP
+ move $usp,[reg+0x66] ; USP
+
+;; get the serial character (from debugport.c) and check if it is a ctrl-c
+
+ jsr getDebugChar
+ cmp.b 3, $r10
+ bne goback
+ nop
+
+;;
+;; Handle the communication
+;;
+ move.d internal_stack+1020,$sp ; Use the internal stack
+ moveq 2,$r10 ; SIGINT
+ jsr handle_exception ; Interactive routine
+
+goback:
+;;
+;; Return to the caller
+;;
+ move.d [reg],$r0 ; Restore R0
+ move.d [reg+0x04],$r1 ; Restore R1
+ move.d [reg+0x08],$r2 ; Restore R2
+ move.d [reg+0x0C],$r3 ; Restore R3
+ move.d [reg+0x10],$r4 ; Restore R4
+ move.d [reg+0x14],$r5 ; Restore R5
+ move.d [reg+0x18],$r6 ; Restore R6
+ move.d [reg+0x1C],$r7 ; Restore R7
+ move.d [reg+0x20],$r8 ; Restore R8
+ move.d [reg+0x24],$r9 ; Restore R9
+ move.d [reg+0x28],$r10 ; Restore R10
+ move.d [reg+0x2C],$r11 ; Restore R11
+ move.d [reg+0x30],$r12 ; Restore R12
+ move.d [reg+0x34],$r13 ; Restore R13
+;;
+;; FIXME: Which registers should be restored?
+;;
+ move.d [reg+0x38],$sp ; Restore SP (R14)
+ move [reg+0x56],$srp ; Restore the subroutine return pointer.
+ move [reg+0x5E],$dccr ; Restore DCCR
+ move [reg+0x66],$usp ; Restore USP
+ reti ; Return from the interrupt routine
+ nop
+");
+
+/* Use this static breakpoint in the start-up only. */
+
+void
+breakpoint(void)
+{
+ kgdb_started = 1;
+ is_dyn_brkp = 0; /* This is a static, not a dynamic breakpoint. */
+ __asm__ volatile ("break 8"); /* Jump to handle_breakpoint. */
+}
+
+/* initialize kgdb. doesn't break into the debugger, but sets up irq and ports */
+
+void
+kgdb_init(void)
+{
+ /* could initialize debug port as well but it's done in head.S already... */
+
+ /* breakpoint handler is now set in irq.c */
+ set_int_vector(8, kgdb_handle_serial);
+
+ enableDebugIRQ();
+}
+
+/****************************** End of file **********************************/
diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c
new file mode 100644
index 00000000000..87ff3779082
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/process.c
@@ -0,0 +1,270 @@
+/* $Id: process.c,v 1.9 2004/10/19 13:07:37 starvik Exp $
+ *
+ * linux/arch/cris/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 2000-2002 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ * Mikael Starvik (starvik@axis.com)
+ *
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/arch/svinto.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_ETRAX_GPIO
+void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
+#endif
+
+/*
+ * We use this if we don't have any better
+ * idle routine..
+ */
+void default_idle(void)
+{
+#ifdef CONFIG_ETRAX_GPIO
+ etrax_gpio_wake_up_check();
+#endif
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+
+void exit_thread(void)
+{
+ /* Nothing needs to be done. */
+}
+
+/* if the watchdog is enabled, we can simply disable interrupts and go
+ * into an eternal loop, and the watchdog will reset the CPU after 0.1s
+ * if on the other hand the watchdog wasn't enabled, we just enable it and wait
+ */
+
+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) && !defined(CONFIG_SVINTO_SIM)
+ extern int cause_of_death;
+#endif
+
+ printk("*** HARD RESET ***\n");
+ local_irq_disable();
+
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ cause_of_death = 0xbedead;
+#else
+ /* Since we dont plan to keep on reseting the watchdog,
+ the key can be arbitrary hence three */
+ *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, 3) |
+ IO_STATE(R_WATCHDOG, enable, start);
+#endif
+
+ while(1) /* waiting for RETRIBUTION! */ ;
+}
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+unsigned long thread_saved_pc(struct task_struct *t)
+{
+ return (unsigned long)user_regs(t->thread_info)->irp;
+}
+
+static void kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
+{
+ fn(arg);
+ do_exit(-1); /* Should never be called, return bad exit value */
+}
+
+/*
+ * Create a kernel thread
+ */
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ /* Don't use r10 since that is set to 0 in copy_thread */
+ regs.r11 = (unsigned long)fn;
+ regs.r12 = (unsigned long)arg;
+ regs.irp = (unsigned long)kernel_thread_helper;
+
+ /* Ok, create the new process.. */
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
+}
+
+/* setup the child's kernel stack with a pt_regs and switch_stack on it.
+ * it will be un-nested 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.
+ *
+ */
+asmlinkage void ret_from_fork(void);
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct pt_regs * childregs;
+ struct switch_stack *swstack;
+
+ /* put the pt_regs structure at the end of the new kernel stack page and fix it up
+ * remember that the task_struct doubles as the kernel stack for the task
+ */
+
+ childregs = user_regs(p->thread_info);
+
+ *childregs = *regs; /* struct copy of pt_regs */
+
+ p->set_child_tid = p->clear_child_tid = NULL;
+
+ childregs->r10 = 0; /* child returns 0 after a fork/clone */
+
+ /* put the switch stack right below the pt_regs */
+
+ swstack = ((struct switch_stack *)childregs) - 1;
+
+ swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
+
+ /* we want to return into ret_from_sys_call after the _resume */
+
+ swstack->return_ip = (unsigned long) ret_from_fork; /* Will call ret_from_sys_call */
+
+ /* fix the user-mode stackpointer */
+
+ p->thread.usp = usp;
+
+ /* and the kernel-mode one */
+
+ p->thread.ksp = (unsigned long) swstack;
+
+#ifdef DEBUG
+ printk("copy_thread: new regs at 0x%p, as shown below:\n", childregs);
+ show_registers(childregs);
+#endif
+
+ return 0;
+}
+
+/*
+ * Be aware of the "magic" 7th argument in the four system-calls below.
+ * They need the latest stackframe, which is put as the 7th argument by
+ * entry.S. The previous arguments are dummies or actually used, but need
+ * to be defined to reach the 7th argument.
+ *
+ * N.B.: Another method to get the stackframe is to use current_regs(). But
+ * it returns the latest stack-frame stacked when going from _user mode_ and
+ * some of these (at least sys_clone) are called from kernel-mode sometimes
+ * (for example during kernel_thread, above) and thus cannot use it. Thus,
+ * to be sure not to get any surprises, we use the method for the other calls
+ * as well.
+ */
+
+asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
+ struct pt_regs *regs)
+{
+ return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL);
+}
+
+/* if newusp is 0, we just grab the old usp */
+/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
+asmlinkage int sys_clone(unsigned long newusp, unsigned long flags,
+ int* parent_tid, int* child_tid, long mof, long srp,
+ struct pt_regs *regs)
+{
+ if (!newusp)
+ newusp = rdusp();
+ return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
+}
+
+/* vfork is a system call in i386 because of register-pressure - maybe
+ * we can remove it and handle it in libc but we put it here until then.
+ */
+
+asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
+ struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(const char *fname, char **argv, char **envp,
+ long r13, long mof, long srp,
+ struct pt_regs *regs)
+{
+ int error;
+ char *filename;
+
+ filename = getname(fname);
+ error = PTR_ERR(filename);
+
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, regs);
+ putname(filename);
+ out:
+ return error;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+#if 0
+ /* YURGH. TODO. */
+
+ unsigned long ebp, esp, eip;
+ unsigned long stack_page;
+ int count = 0;
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+ stack_page = (unsigned long)p;
+ esp = p->thread.esp;
+ if (!stack_page || esp < stack_page || esp > 8188+stack_page)
+ return 0;
+ /* include/asm-i386/system.h:switch_to() pushes ebp last. */
+ ebp = *(unsigned long *) esp;
+ do {
+ if (ebp < stack_page || ebp > 8184+stack_page)
+ return 0;
+ eip = *(unsigned long *) (ebp+4);
+ if (!in_sched_functions(eip))
+ return eip;
+ ebp = *(unsigned long *) ebp;
+ } while (count++ < 16);
+#endif
+ return 0;
+}
+#undef last_sched
+#undef first_sched
+
+void show_regs(struct pt_regs * regs)
+{
+ unsigned long usp = rdusp();
+ printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
+ regs->irp, regs->srp, regs->dccr, usp, regs->mof );
+ printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ 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-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c
new file mode 100644
index 00000000000..da15db8ae48
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/ptrace.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2000-2003, Axis Communications AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+
+/*
+ * Determines which bits in DCCR the user has access to.
+ * 1 = access, 0 = no access.
+ */
+#define DCCR_MASK 0x0000001f /* XNZVC */
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+inline 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
+ */
+
+ if (regno == PT_USP)
+ return task->thread.usp;
+ else if (regno < PT_MAX)
+ return ((unsigned long *)user_regs(task->thread_info))[regno];
+ else
+ return 0;
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+inline int put_reg(struct task_struct *task, unsigned int regno,
+ unsigned long data)
+{
+ if (regno == PT_USP)
+ task->thread.usp = data;
+ else if (regno < PT_MAX)
+ ((unsigned long *)user_regs(task->thread_info))[regno] = data;
+ else
+ return -1;
+ return 0;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching.
+ *
+ * Make sure the single step bit is not set.
+ */
+void
+ptrace_disable(struct task_struct *child)
+{
+ /* Todo - pending singlesteps? */
+}
+
+/*
+ * Note that this implementation of ptrace behaves differently from vanilla
+ * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT,
+ * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
+ * ignored. Instead, the data variable is expected to point at a location
+ * (in user space) where the result of the ptrace call is written (instead of
+ * being returned).
+ */
+asmlinkage int
+sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret;
+ unsigned long __user *datap = (unsigned long __user *)data;
+
+ lock_kernel();
+ ret = -EPERM;
+
+ if (request == PTRACE_TRACEME) {
+ if (current->ptrace & PT_PTRACED)
+ goto out;
+
+ current->ptrace |= PT_PTRACED;
+ ret = 0;
+ goto out;
+ }
+
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+
+ if (child)
+ get_task_struct(child);
+
+ read_unlock(&tasklist_lock);
+
+ if (!child)
+ goto out;
+
+ ret = -EPERM;
+
+ if (pid == 1) /* Leave the init process alone! */
+ goto out_tsk;
+
+ if (request == PTRACE_ATTACH) {
+ ret = ptrace_attach(child);
+ goto out_tsk;
+ }
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
+ goto out_tsk;
+
+ switch (request) {
+ /* Read word at location address. */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+
+ 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) || addr < 0 || addr > PT_MAX << 2)
+ break;
+
+ tmp = get_reg(child, addr >> 2);
+ ret = put_user(tmp, datap);
+ break;
+ }
+
+ /* Write the word at location address. */
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = 0;
+
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ break;
+
+ ret = -EIO;
+ break;
+
+ /* Write the word at location address in the USER area. */
+ case PTRACE_POKEUSR:
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
+ break;
+
+ addr >>= 2;
+
+ if (addr == PT_DCCR) {
+ /* don't allow the tracing process to change stuff like
+ * interrupt enable, kernel/user bit, dma enables etc.
+ */
+ data &= DCCR_MASK;
+ data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
+ }
+ if (put_reg(child, addr, data))
+ break;
+ ret = 0;
+ break;
+
+ case PTRACE_SYSCALL:
+ case PTRACE_CONT:
+ ret = -EIO;
+
+ if ((unsigned long) data > _NSIG)
+ break;
+
+ if (request == PTRACE_SYSCALL) {
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ }
+ else {
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ }
+
+ child->exit_code = data;
+
+ /* TODO: make sure any pending breakpoint is killed */
+ wake_up_process(child);
+ ret = 0;
+
+ break;
+
+ /* Make the child exit by sending it a sigkill. */
+ case PTRACE_KILL:
+ ret = 0;
+
+ if (child->state == TASK_ZOMBIE)
+ break;
+
+ child->exit_code = SIGKILL;
+
+ /* TODO: make sure any pending breakpoint is killed */
+ wake_up_process(child);
+ break;
+
+ /* Set the trap flag. */
+ case PTRACE_SINGLESTEP:
+ ret = -EIO;
+
+ if ((unsigned long) data > _NSIG)
+ break;
+
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+
+ /* TODO: set some clever breakpoint mechanism... */
+
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+
+ case PTRACE_DETACH:
+ ret = ptrace_detach(child, data);
+ 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;
+ }
+
+ data += sizeof(long);
+ }
+
+ 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_DCCR) {
+ tmp &= DCCR_MASK;
+ tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
+ }
+
+ put_reg(child, i, tmp);
+ data += sizeof(long);
+ }
+
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+out_tsk:
+ put_task_struct(child);
+out:
+ unlock_kernel();
+ 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;
+ }
+}
diff --git a/arch/cris/arch-v10/kernel/setup.c b/arch/cris/arch-v10/kernel/setup.c
new file mode 100644
index 00000000000..b668d7fb68e
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/setup.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * linux/arch/cris/arch-v10/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (c) 2001-2002 Axis Communications AB
+ */
+
+/*
+ * This file handles the architecture-dependent parts of initialization
+ */
+
+#include <linux/config.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.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
+
+static struct cpu_info {
+ char *model;
+ unsigned short cache;
+ unsigned short flags;
+} cpu_info[] = {
+ /* The first four models will never ever run this code and are
+ only here for display. */
+ { "ETRAX 1", 0, 0 },
+ { "ETRAX 2", 0, 0 },
+ { "ETRAX 3", 0, HAS_TOKENRING },
+ { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI },
+ { "Unknown", 0, 0 },
+ { "Unknown", 0, 0 },
+ { "Unknown", 0, 0 },
+ { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
+ { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
+ { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
+ { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
+ { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+ { "Unknown", 0, 0 } /* This entry MUST be the last */
+};
+
+int show_cpuinfo(struct seq_file *m, void *v)
+{
+ unsigned long revision;
+ struct cpu_info *info;
+
+ /* read the version register in the CPU and print some stuff */
+
+ revision = rdvr();
+
+ if (revision >= sizeof cpu_info/sizeof *cpu_info)
+ info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1];
+ else
+ info = &cpu_info[revision];
+
+ return seq_printf(m,
+ "processor\t: 0\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",
+
+ revision,
+ info->model,
+ info->cache,
+ 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);
+}
+
+#endif /* CONFIG_PROC_FS */
+
+void
+show_etrax_copyright(void)
+{
+ printk(KERN_INFO
+ "Linux/CRIS port on ETRAX 100LX (c) 2001 Axis Communications AB\n");
+}
diff --git a/arch/cris/arch-v10/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c
new file mode 100644
index 00000000000..561a890a8e4
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/shadows.c
@@ -0,0 +1,36 @@
+/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+ *
+ * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
+ */
+
+/* Shadows for internal Etrax-registers */
+
+unsigned long genconfig_shadow;
+unsigned long port_g_data_shadow;
+unsigned char port_pa_dir_shadow;
+unsigned char port_pa_data_shadow;
+unsigned char port_pb_i2c_shadow;
+unsigned char port_pb_config_shadow;
+unsigned char port_pb_dir_shadow;
+unsigned char port_pb_data_shadow;
+unsigned long r_timer_ctrl_shadow;
+
+/* Shadows for external I/O port registers.
+ * These are only usable if there actually IS a latch connected
+ * to the corresponding external chip-select pin.
+ *
+ * A common usage is that CSP0 controls LED's and CSP4 video chips.
+ */
+
+unsigned long port_cse1_shadow;
+unsigned long port_csp0_shadow;
+unsigned long port_csp4_shadow;
+
+/* Corresponding addresses for the ports.
+ * These are initialized in arch/cris/mm/init.c using ioremap.
+ */
+
+volatile unsigned long *port_cse1_addr;
+volatile unsigned long *port_csp0_addr;
+volatile unsigned long *port_csp4_addr;
+
diff --git a/arch/cris/arch-v10/kernel/signal.c b/arch/cris/arch-v10/kernel/signal.c
new file mode 100644
index 00000000000..85e0032e664
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/signal.c
@@ -0,0 +1,580 @@
+/*
+ * linux/arch/cris/kernel/signal.c
+ *
+ * Based on arch/i386/kernel/signal.c by
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson *
+ *
+ * Ideas also taken from arch/arm.
+ *
+ * Copyright (C) 2000, 2001 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.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 <asm/processor.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */
+/* manipulate regs so that upon return, it will be re-executed */
+
+/* We rely on that pc points to the instruction after "break 13", so the
+ * library must never do strange things like putting it in a delay slot.
+ */
+#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2;
+
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal. Define
+ * dummy arguments to be able to reach the regs argument. (Note that this
+ * arrangement relies on old_sigset_t occupying one register.)
+ */
+int
+sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
+ long srp, struct pt_regs *regs)
+{
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->r10 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(0, &saveset, regs))
+ /* We will get here twice: once to call the signal
+ handler, then again to return from the
+ sigsuspend system call. When calling the
+ signal handler, R10 holds the signal number as
+ set through do_signal. The sigsuspend call
+ will return with the restored value set above;
+ always -EINTR. */
+ return regs->r10;
+ }
+}
+
+/* Define dummy arguments to be able to reach the regs argument. (Note that
+ * this arrangement relies on size_t occupying one register.)
+ */
+int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13,
+ long mof, long srp, struct pt_regs *regs)
+{
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+
+ spin_lock_irq(&current->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ regs->r10 = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(0, &saveset, regs))
+ /* We will get here twice: once to call the signal
+ handler, then again to return from the
+ sigsuspend system call. When calling the
+ signal handler, R10 holds the signal number as
+ set through do_signal. The sigsuspend call
+ will return with the restored value set above;
+ always -EINTR. */
+ return regs->r10;
+ }
+}
+
+int
+sys_sigaction(int sig, const struct old_sigaction __user *act,
+ struct old_sigaction *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+int
+sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
+{
+ return do_sigaltstack(uss, uoss, rdusp());
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe {
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS-1];
+ unsigned char retcode[8]; /* trampoline code */
+};
+
+struct rt_sigframe {
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ unsigned char retcode[8]; /* trampoline code */
+};
+
+
+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_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /* restore the regs from &sc->regs (same as sc, since regs is first)
+ * (sc is already checked for VERIFY_READ since the sigframe was
+ * checked in sys_sigreturn previously)
+ */
+
+ if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
+ goto badframe;
+
+ /* make sure the U-flag is set so user-mode cannot fool us */
+
+ regs->dccr |= 1 << 8;
+
+ /* restore the old USP as it was before we stacked the sc etc.
+ * (we cannot just pop the sigcontext since we aligned the sp and
+ * stuff after pushing it)
+ */
+
+ err |= __get_user(old_usp, &sc->usp);
+
+ wrusp(old_usp);
+
+ /* TODO: the other ports use regs->orig_XX to disable syscall checks
+ * after this completes, but we don't use that mechanism. maybe we can
+ * use it now ?
+ */
+
+ return err;
+
+badframe:
+ return 1;
+}
+
+/* Define dummy arguments to be able to reach the regs argument. */
+
+asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
+ long srp, struct pt_regs *regs)
+{
+ struct sigframe __user *frame = (struct sigframe *)rdusp();
+ sigset_t set;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ 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;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->sc))
+ goto badframe;
+
+ /* TODO: SIGTRAP when single-stepping as in arm ? */
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/* Define dummy arguments to be able to reach the regs argument. */
+
+asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
+ long mof, long srp, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame = (struct rt_sigframe *)rdusp();
+ sigset_t set;
+
+ /*
+ * Since we stacked the signal on a dword boundary,
+ * then frame should be dword aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ 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;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT)
+ goto badframe;
+
+ return regs->r10;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask)
+{
+ int err = 0;
+ unsigned long usp = rdusp();
+
+ /* copy the regs. they are first in sc so we can use sc directly */
+
+ err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
+
+ /* Set the frametype to CRIS_FRAME_NORMAL for the execution of
+ the signal handler. The frametype will be restored to its previous
+ value in restore_sigcontext. */
+ regs->frametype = CRIS_FRAME_NORMAL;
+
+ /* then some other stuff */
+
+ err |= __put_user(mask, &sc->oldmask);
+
+ err |= __put_user(usp, &sc->usp);
+
+ return err;
+}
+
+/* figure out where we want to put the new signal frame - usually on the stack */
+
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
+{
+ unsigned long sp = rdusp();
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* make sure the frame is dword-aligned */
+
+ sp &= ~3;
+
+ return (void __user*)(sp - frame_size);
+}
+
+/* grab and setup a signal frame.
+ *
+ * basically we stack a lot of state info, and arrange for the
+ * user-mode program to return to the kernel using either a
+ * trampoline which performs the syscall sigreturn, or a provided
+ * user-mode trampoline.
+ */
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs * regs)
+{
+ struct sigframe __user *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+ if (err)
+ goto give_sigsegv;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* This is movu.w __NR_sigreturn, r9; break 13; */
+ 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)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+
+ regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->srp = return_ip; /* what we enter LATER */
+ regs->r10 = sig; /* first argument is signo */
+
+ /* actually move the usp to reflect the stacked frame */
+
+ wrusp((unsigned long)frame);
+
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+}
+
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs * regs)
+{
+ struct rt_sigframe __user *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
+
+ /* 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));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ka->sa.sa_restorer;
+ } else {
+ /* trampoline - the desired return ip is the retcode itself */
+ return_ip = (unsigned long)&frame->retcode;
+ /* This is movu.w __NR_rt_sigreturn, r9; break 13; */
+ 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)
+ goto give_sigsegv;
+
+ /* TODO what is the current->exec_domain stuff and invmap ? */
+
+ /* Set up registers for signal handler */
+
+ regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
+ regs->srp = return_ip; /* what we enter LATER */
+ regs->r10 = sig; /* first argument is signo */
+ regs->r11 = (unsigned long) &frame->info; /* second argument is (siginfo_t *) */
+ regs->r12 = 0; /* third argument is unused */
+
+ /* actually move the usp to reflect the stacked frame */
+
+ wrusp((unsigned long)frame);
+
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+extern inline void
+handle_signal(int canrestart, unsigned long sig,
+ siginfo_t *info, struct k_sigaction *ka,
+ sigset_t *oldset, struct pt_regs * regs)
+{
+ /* Are we from a system call? */
+ if (canrestart) {
+ /* If so, check system call restarting.. */
+ switch (regs->r10) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ /* ERESTARTNOHAND means that the syscall should only be
+ restarted if there was no handler for the signal, and since
+ we only get here if there is a handler, we don't restart */
+ regs->r10 = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ /* ERESTARTSYS means to restart the syscall if there is no
+ handler or the handler was registered with SA_RESTART */
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->r10 = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* ERESTARTNOINTR means that the syscall should be called again
+ after the signal handler returns. */
+ RESTART_CRIS_SYS(regs);
+ }
+ }
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ setup_frame(sig, ka, oldset, regs);
+
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+}
+
+/*
+ * 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.
+ */
+
+int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 1;
+
+ if (!oldset)
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(canrestart, signr, &info, &ka, oldset, regs);
+ return 1;
+ }
+
+ /* Did we come 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->r10 = __NR_restart_syscall;
+ regs->irp -= 2;
+ }
+ }
+ return 0;
+}
diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c
new file mode 100644
index 00000000000..6b7b4e0802e
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/time.c
@@ -0,0 +1,369 @@
+/* $Id: time.c,v 1.5 2004/09/29 06:12:46 starvik Exp $
+ *
+ * linux/arch/cris/arch-v10/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ * Copyright (C) 1999-2002 Axis Communications AB
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/timex.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/swap.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <asm/arch/svinto.h>
+#include <asm/types.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/rtc.h>
+
+/* define this if you need to use print_timestamp */
+/* it will make jiffies at 96 hz instead of 100 hz though */
+#undef USE_CASCADE_TIMERS
+
+extern void update_xtime_from_cmos(void);
+extern int set_rtc_mmss(unsigned long nowtime);
+extern int setup_irq(int, struct irqaction *);
+extern int have_rtc;
+
+unsigned long get_ns_in_jiffie(void)
+{
+ unsigned char timer_count, t1;
+ unsigned short presc_count;
+ unsigned long ns;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ local_irq_disable();
+ timer_count = *R_TIMER0_DATA;
+ presc_count = *R_TIM_PRESC_STATUS;
+ /* presc_count might be wrapped */
+ t1 = *R_TIMER0_DATA;
+
+ if (timer_count != t1){
+ /* it wrapped, read prescaler again... */
+ presc_count = *R_TIM_PRESC_STATUS;
+ timer_count = t1;
+ }
+ local_irq_restore(flags);
+ if (presc_count >= PRESCALE_VALUE/2 ){
+ presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2;
+ } else {
+ presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2;
+ }
+
+ ns = ( (TIMER0_DIV - timer_count) * ((1000000000/HZ)/TIMER0_DIV )) +
+ ( (presc_count) * (1000000000/PRESCALE_FREQ));
+ return ns;
+}
+
+unsigned long do_slow_gettimeoffset(void)
+{
+ unsigned long count, t1;
+ unsigned long usec_count = 0;
+ unsigned short presc_count;
+
+ static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */
+ static unsigned long jiffies_p = 0;
+
+ /*
+ * cache volatile jiffies temporarily; we have IRQs turned off.
+ */
+ unsigned long jiffies_t;
+
+ /* The timer interrupt comes from Etrax timer 0. In order to get
+ * better precision, we check the current value. It might have
+ * underflowed already though.
+ */
+
+#ifndef CONFIG_SVINTO_SIM
+ /* Not available in the xsim simulator. */
+ count = *R_TIMER0_DATA;
+ presc_count = *R_TIM_PRESC_STATUS;
+ /* presc_count might be wrapped */
+ t1 = *R_TIMER0_DATA;
+ if (count != t1){
+ /* it wrapped, read prescaler again... */
+ presc_count = *R_TIM_PRESC_STATUS;
+ count = t1;
+ }
+#else
+ count = 0;
+ presc_count = 0;
+#endif
+
+ jiffies_t = jiffies;
+
+ /*
+ * avoiding timer inconsistencies (they are rare, but they happen)...
+ * there are one problem that must be avoided here:
+ * 1. the timer counter underflows
+ */
+ if( jiffies_t == jiffies_p ) {
+ if( count > count_p ) {
+ /* Timer wrapped, use new count and prescale
+ * increase the time corresponding to one jiffie
+ */
+ usec_count = 1000000/HZ;
+ }
+ } else
+ jiffies_p = jiffies_t;
+ count_p = count;
+ if (presc_count >= PRESCALE_VALUE/2 ){
+ presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2;
+ } else {
+ presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2;
+ }
+ /* Convert timer value to usec */
+ usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) +
+ (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000);
+
+ return usec_count;
+}
+
+/* Excerpt from the Etrax100 HSDD about the built-in watchdog:
+ *
+ * 3.10.4 Watchdog timer
+
+ * When the watchdog timer is started, it generates an NMI if the watchdog
+ * isn't restarted or stopped within 0.1 s. If it still isn't restarted or
+ * stopped after an additional 3.3 ms, the watchdog resets the chip.
+ * The watchdog timer is stopped after reset. The watchdog timer is controlled
+ * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit
+ * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is
+ * described in the table below:
+ *
+ * Watchdog Value written:
+ * state: To enable: To key: Operation:
+ * -------- ---------- ------- ----------
+ * stopped 0 X No effect.
+ * stopped 1 key_val Start watchdog with key = key_val.
+ * started 0 ~key Stop watchdog
+ * started 1 ~key Restart watchdog with key = ~key.
+ * started X new_key_val Change key to new_key_val.
+ *
+ * Note: '~' is the bitwise NOT operator.
+ *
+ */
+
+/* right now, starting the watchdog is the same as resetting it */
+#define start_watchdog reset_watchdog
+
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+static int watchdog_key = 0; /* arbitrary number */
+#endif
+
+/* number of pages to consider "out of memory". it is normal that the memory
+ * is used though, so put this really low.
+ */
+
+#define WATCHDOG_MIN_FREE_PAGES 8
+
+void
+reset_watchdog(void)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ /* only keep watchdog happy as long as we have memory left! */
+ if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
+ /* reset the watchdog with the inverse of the old key */
+ watchdog_key ^= 0x7; /* invert key, which is 3 bits */
+ *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
+ IO_STATE(R_WATCHDOG, enable, start);
+ }
+#endif
+}
+
+/* stop the watchdog - we still need the correct key */
+
+void
+stop_watchdog(void)
+{
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ watchdog_key ^= 0x7; /* invert key, which is 3 bits */
+ *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
+ IO_STATE(R_WATCHDOG, enable, stop);
+#endif
+}
+
+/* last time the cmos clock got updated */
+static long last_rtc_update = 0;
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+
+//static unsigned short myjiff; /* used by our debug routine print_timestamp */
+
+extern void cris_do_profile(struct pt_regs *regs);
+
+static inline irqreturn_t
+timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* acknowledge the timer irq */
+
+#ifdef USE_CASCADE_TIMERS
+ *R_TIMER_CTRL =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, clr) |
+ IO_STATE( R_TIMER_CTRL, tm1, run) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, clr) |
+ IO_STATE( R_TIMER_CTRL, tm0, run) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+#else
+ *R_TIMER_CTRL = r_timer_ctrl_shadow |
+ IO_STATE(R_TIMER_CTRL, i0, clr);
+#endif
+
+ /* reset watchdog otherwise it resets us! */
+
+ reset_watchdog();
+
+ /* call the real timer interrupt handler */
+
+ do_timer(regs);
+
+ cris_do_profile(regs); /* Save profiling information */
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ *
+ * The division here is not time critical since it will run once in
+ * 11 minutes
+ */
+ if ((time_status & STA_UNSYNC) == 0 &&
+ xtime.tv_sec > last_rtc_update + 660 &&
+ (xtime.tv_nsec / 1000) >= 500000 - (tick_nsec / 1000) / 2 &&
+ (xtime.tv_nsec / 1000) <= 500000 + (tick_nsec / 1000) / 2) {
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
+ return IRQ_HANDLED;
+}
+
+/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain
+ * it needs to be SA_INTERRUPT to make the jiffies update work properly
+ */
+
+static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT,
+ CPU_MASK_NONE, "timer", NULL, NULL};
+
+void __init
+time_init(void)
+{
+ /* 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;
+
+ if(RTC_INIT() < 0) {
+ /* no RTC, start at 1980 */
+ xtime.tv_sec = 0;
+ xtime.tv_nsec = 0;
+ have_rtc = 0;
+ } else {
+ /* get the current time */
+ have_rtc = 1;
+ update_xtime_from_cmos();
+ }
+
+ /*
+ * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the
+ * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC).
+ */
+ set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
+
+ /* Setup the etrax timers
+ * Base frequency is 25000 hz, divider 250 -> 100 HZ
+ * In normal mode, we use timer0, so timer1 is free. In cascade
+ * mode (which we sometimes use for debugging) both timers are used.
+ * Remember that linux/timex.h contains #defines that rely on the
+ * timer settings below (hz and divide factor) !!!
+ */
+
+#ifdef USE_CASCADE_TIMERS
+ *R_TIMER_CTRL =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, nop) |
+ IO_STATE( R_TIMER_CTRL, tm1, stop_ld) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, nop) |
+ IO_STATE( R_TIMER_CTRL, tm0, stop_ld) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
+ IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
+ IO_STATE( R_TIMER_CTRL, i1, nop) |
+ IO_STATE( R_TIMER_CTRL, tm1, run) |
+ IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
+ IO_STATE( R_TIMER_CTRL, i0, nop) |
+ IO_STATE( R_TIMER_CTRL, tm0, run) |
+ IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
+#else
+ *R_TIMER_CTRL =
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) |
+ IO_STATE(R_TIMER_CTRL, i1, nop) |
+ IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
+ IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
+ IO_STATE(R_TIMER_CTRL, i0, nop) |
+ IO_STATE(R_TIMER_CTRL, tm0, stop_ld) |
+ IO_STATE(R_TIMER_CTRL, clksel0, flexible);
+
+ *R_TIMER_CTRL = r_timer_ctrl_shadow =
+ IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
+ IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) |
+ IO_STATE(R_TIMER_CTRL, i1, nop) |
+ IO_STATE(R_TIMER_CTRL, tm1, run) |
+ IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
+ IO_STATE(R_TIMER_CTRL, i0, nop) |
+ IO_STATE(R_TIMER_CTRL, tm0, run) |
+ IO_STATE(R_TIMER_CTRL, clksel0, flexible);
+
+ *R_TIMER_PRESCALE = PRESCALE_VALUE;
+#endif
+
+ *R_IRQ_MASK0_SET =
+ IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */
+
+ /* now actually register the timer irq handler that calls timer_interrupt() */
+
+ setup_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */
+
+ /* enable watchdog if we should use one */
+
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+ printk("Enabling watchdog...\n");
+ start_watchdog();
+
+ /* If we use the hardware watchdog, we want to trap it as an NMI
+ and dump registers before it resets us. For this to happen, we
+ must set the "m" NMI enable flag (which once set, is unset only
+ when an NMI is taken).
+
+ The same goes for the external NMI, but that doesn't have any
+ driver or infrastructure support yet. */
+ asm ("setf m");
+
+ *R_IRQ_MASK0_SET =
+ IO_STATE(R_IRQ_MASK0_SET, watchdog_nmi, set);
+ *R_VECT_MASK_SET =
+ IO_STATE(R_VECT_MASK_SET, nmi, set);
+#endif
+}
diff --git a/arch/cris/arch-v10/kernel/traps.c b/arch/cris/arch-v10/kernel/traps.c
new file mode 100644
index 00000000000..da491f438a6
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/traps.c
@@ -0,0 +1,132 @@
+/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $
+ *
+ * linux/arch/cris/arch-v10/traps.c
+ *
+ * Heler functions for trap handlers
+ *
+ * Copyright (C) 2000-2002 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ * Hans-Peter Nilsson
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/arch/sv_addr_ag.h>
+
+void
+show_registers(struct pt_regs * regs)
+{
+ /* We either use rdusp() - the USP register, which might not
+ correspond to the current process for all cases we're called,
+ or we use the current->thread.usp, which is not up to date for
+ the current process. Experience shows we want the USP
+ register. */
+ unsigned long usp = rdusp();
+
+ printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
+ regs->irp, regs->srp, regs->dccr, usp, regs->mof );
+ printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ 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);
+ printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
+ printk("Process %s (pid: %d, stackpage=%08lx)\n",
+ current->comm, current->pid, (unsigned long)current);
+
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (! user_mode(regs)) {
+ int i;
+
+ show_stack(NULL, (unsigned long*)usp);
+
+ /* Dump kernel stack if the previous dump wasn't one. */
+ if (usp != 0)
+ show_stack (NULL, NULL);
+
+ printk("\nCode: ");
+ if(regs->irp < PAGE_OFFSET)
+ goto bad;
+
+ /* Often enough the value at regs->irp does not point to
+ the interesting instruction, which is most often the
+ _previous_ instruction. So we dump at an offset large
+ enough that instruction decoding should be in sync at
+ the interesting point, but small enough to fit on a row
+ (sort of). We point out the regs->irp location in a
+ ksymoops-friendly way by wrapping the byte for that
+ address in parentheses. */
+ for(i = -12; i < 12; i++)
+ {
+ unsigned char c;
+ if(__get_user(c, &((unsigned char*)regs->irp)[i])) {
+bad:
+ printk(" Bad IP value.");
+ break;
+ }
+
+ if (i == 0)
+ printk("(%02x) ", c);
+ else
+ printk("%02x ", c);
+ }
+ printk("\n");
+ }
+}
+
+/* Called from entry.S when the watchdog has bitten
+ * We print out something resembling an oops dump, and if
+ * we have the nice doggy development flag set, we halt here
+ * instead of rebooting.
+ */
+
+extern void reset_watchdog(void);
+extern void stop_watchdog(void);
+
+
+void
+watchdog_bite_hook(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ local_irq_disable();
+ stop_watchdog();
+ show_registers(regs);
+ while(1) /* nothing */;
+#else
+ show_registers(regs);
+#endif
+}
+
+/* This is normally the 'Oops' routine */
+void
+die_if_kernel(const char * str, struct pt_regs * regs, long err)
+{
+ if(user_mode(regs))
+ return;
+
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ /* This printout might take too long and trigger the
+ * watchdog normally. If we're in the nice doggy
+ * development mode, stop the watchdog during printout.
+ */
+ stop_watchdog();
+#endif
+
+ printk("%s: %04lx\n", str, err & 0xffff);
+
+ show_registers(regs);
+
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+ reset_watchdog();
+#endif
+ do_exit(SIGSEGV);
+}