summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v10/kernel
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /arch/cris/arch-v10/kernel
Initial import
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/crisksyms.c16
-rw-r--r--arch/cris/arch-v10/kernel/debugport.c559
-rw-r--r--arch/cris/arch-v10/kernel/dma.c287
-rw-r--r--arch/cris/arch-v10/kernel/entry.S969
-rw-r--r--arch/cris/arch-v10/kernel/fasttimer.c834
-rw-r--r--arch/cris/arch-v10/kernel/head.S723
-rw-r--r--arch/cris/arch-v10/kernel/io_interface_mux.c1182
-rw-r--r--arch/cris/arch-v10/kernel/irq.c235
-rw-r--r--arch/cris/arch-v10/kernel/kgdb.c1144
-rw-r--r--arch/cris/arch-v10/kernel/process.c193
-rw-r--r--arch/cris/arch-v10/kernel/ptrace.c202
-rw-r--r--arch/cris/arch-v10/kernel/setup.c106
-rw-r--r--arch/cris/arch-v10/kernel/shadows.c36
-rw-r--r--arch/cris/arch-v10/kernel/signal.c438
-rw-r--r--arch/cris/arch-v10/kernel/time.c267
-rw-r--r--arch/cris/arch-v10/kernel/traps.c131
17 files changed, 7339 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/kernel/Makefile b/arch/cris/arch-v10/kernel/Makefile
new file mode 100644
index 000000000..4841e822c
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/Makefile
@@ -0,0 +1,17 @@
+#
+# 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 \
+ dma.o io_interface_mux.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/crisksyms.c b/arch/cris/arch-v10/kernel/crisksyms.c
new file mode 100644
index 000000000..1ca6fc283
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/crisksyms.c
@@ -0,0 +1,16 @@
+#include <linux/module.h>
+#include <asm/io.h>
+#include <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 000000000..7d307cce8
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/debugport.c
@@ -0,0 +1,559 @@
+/* Serialport functions for debugging
+ *
+ * Copyright (c) 2000-2007 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * Exports:
+ * console_print_etrax(char *buf)
+ * int getDebugChar()
+ * putDebugChar(int)
+ * enableDebugIRQ()
+ * init_etrax_debug()
+ *
+ */
+
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <arch/svinto.h>
+
+extern void reset_watchdog(void);
+
+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),
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 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),
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 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),
+ 0,
+ 115200,
+ 'N',
+ 8
+ },
+ {
+ 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),
+ 0,
+ 115200,
+ 'N',
+ 8
+ }
+};
+
+#ifdef CONFIG_ETRAX_SERIAL
+extern struct tty_driver *serial_driver;
+#endif
+
+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
+
+static struct dbg_port* kgdb_port =
+#if defined(CONFIG_ETRAX_KGDB_PORT0)
+ &ports[0];
+#elif defined(CONFIG_ETRAX_KGDB_PORT1)
+ &ports[1];
+#elif defined(CONFIG_ETRAX_KGDB_PORT2)
+ &ports[2];
+#elif defined(CONFIG_ETRAX_KGDB_PORT3)
+ &ports[3];
+#else
+ NULL;
+#endif
+
+static void
+start_port(struct dbg_port* p)
+{
+ unsigned long rec_ctrl = 0;
+ unsigned long tr_ctrl = 0;
+
+ if (!p)
+ return;
+
+ if (p->started)
+ return;
+ p->started = 1;
+
+ if (p->index == 0)
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+ }
+ else if (p->index == 1)
+ {
+ genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
+ genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+ }
+ else if (p->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;
+
+ *p->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 (p->baudrate)
+ {
+ case 0:
+ case 115200:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
+ break;
+ case 1200:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz);
+ break;
+ case 2400:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz);
+ break;
+ case 4800:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz);
+ break;
+ case 9600:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz);
+ break;
+ case 19200:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz);
+ break;
+ case 38400:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz);
+ break;
+ case 57600:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz);
+ break;
+ default:
+ *p->baud =
+ IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
+ IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
+ break;
+ }
+
+ if (p->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 (p->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 (p->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);
+ }
+
+ *p->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;
+
+ *p->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;
+
+ if (!port)
+ return;
+
+ local_irq_save(flags);
+
+ /* Send data */
+ for (i = 0; i < len; i++) {
+ /* LF -> CRLF */
+ if (buf[i] == '\n') {
+ while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+ ;
+ *port->write = '\r';
+ }
+ /* Wait until transmitter is ready and send.*/
+ while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+ ;
+ *port->write = buf[i];
+ }
+
+ /*
+ * Feed the watchdog, otherwise it will reset the chip during boot.
+ * The time to send an ordinary boot message line (10-90 chars)
+ * varies between 1-8ms at 115200. What makes up for the additional
+ * 90ms that allows the watchdog to bite?
+ */
+ reset_watchdog();
+
+ local_irq_restore(flags);
+}
+
+static void
+console_write(struct console *co, const char *buf, unsigned int len)
+{
+ if (!port)
+ return;
+
+ 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;
+
+ if (!kgdb_port)
+ return 0;
+
+ do {
+ readval = *kgdb_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)
+{
+ if (!kgdb_port)
+ return;
+
+ while (!(*kgdb_port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+ ;
+ *kgdb_port->write = val;
+}
+
+/* Enable irq for receiving chars on the debug port, used by kgdb */
+
+void
+enableDebugIRQ(void)
+{
+ if (!kgdb_port)
+ return;
+
+ *R_IRQ_MASK1_SET = kgdb_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);
+
+ *kgdb_port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
+}
+
+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(0);
+ }
+ return 0;
+}
+
+
+/* This is a dummy serial device that throws away anything written to it.
+ * This is used when no debug output is wanted.
+ */
+static struct tty_driver dummy_driver;
+
+static int dummy_open(struct tty_struct *tty, struct file * filp)
+{
+ return 0;
+}
+
+static void dummy_close(struct tty_struct *tty, struct file * filp)
+{
+}
+
+static int dummy_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ return count;
+}
+
+static int dummy_write_room(struct tty_struct *tty)
+{
+ return 8192;
+}
+
+static const struct tty_operations dummy_ops = {
+ .open = dummy_open,
+ .close = dummy_close,
+ .write = dummy_write,
+ .write_room = dummy_write_room,
+};
+
+void __init
+init_dummy_console(void)
+{
+ memset(&dummy_driver, 0, sizeof(struct tty_driver));
+ dummy_driver.driver_name = "serial";
+ dummy_driver.name = "ttyS";
+ dummy_driver.major = TTY_MAJOR;
+ dummy_driver.minor_start = 68;
+ dummy_driver.num = 1; /* etrax100 has 4 serial ports */
+ dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ dummy_driver.subtype = SERIAL_TYPE_NORMAL;
+ dummy_driver.init_termios = tty_std_termios;
+ /* Normally B9600 default... */
+ dummy_driver.init_termios.c_cflag =
+ B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+ dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ dummy_driver.init_termios.c_ispeed = 115200;
+ dummy_driver.init_termios.c_ospeed = 115200;
+
+ dummy_driver.ops = &dummy_ops;
+ if (tty_register_driver(&dummy_driver))
+ panic("Couldn't register dummy serial driver\n");
+}
+
+static struct tty_driver*
+etrax_console_device(struct console* co, int *index)
+{
+ if (port)
+ *index = port->index;
+ else
+ *index = 0;
+#ifdef CONFIG_ETRAX_SERIAL
+ return port ? serial_driver : &dummy_driver;
+#else
+ return &dummy_driver;
+#endif
+}
+
+static struct console sercons = {
+ 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) {
+ unregister_console(&sercons);
+ register_console(&sercons0);
+ register_console(&sercons1);
+ register_console(&sercons2);
+ register_console(&sercons3);
+ init_dummy_console();
+ return 0;
+ }
+
+ first = 0;
+ register_console(&sercons);
+ start_port(port);
+#ifdef CONFIG_ETRAX_KGDB
+ start_port(kgdb_port);
+#endif
+ return 0;
+}
+__initcall(init_etrax_debug);
diff --git a/arch/cris/arch-v10/kernel/dma.c b/arch/cris/arch-v10/kernel/dma.c
new file mode 100644
index 000000000..579504735
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/dma.c
@@ -0,0 +1,287 @@
+/* Wrapper for DMA channel allocator that updates DMA client muxing.
+ * Copyright 2004-2007, Axis Communications AB
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <asm/dma.h>
+#include <arch/svinto.h>
+#include <arch/system.h>
+
+/* Macro to access ETRAX 100 registers */
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+ IO_STATE_(reg##_, field##_, _##val)
+
+
+static char used_dma_channels[MAX_DMA_CHANNELS];
+static const char * used_dma_channels_users[MAX_DMA_CHANNELS];
+
+int cris_request_dma(unsigned int dmanr, const char * device_id,
+ unsigned options, enum dma_owner owner)
+{
+ unsigned long flags;
+ unsigned long int gens;
+ int fail = -EINVAL;
+
+ if (dmanr >= MAX_DMA_CHANNELS) {
+ printk(KERN_CRIT "cris_request_dma: invalid DMA channel %u\n", dmanr);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+ if (used_dma_channels[dmanr]) {
+ local_irq_restore(flags);
+ if (options & DMA_VERBOSE_ON_ERROR) {
+ printk(KERN_CRIT "Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]);
+ }
+ if (options & DMA_PANIC_ON_ERROR) {
+ panic("request_dma error!");
+ }
+ return -EBUSY;
+ }
+
+ gens = genconfig_shadow;
+
+ switch(owner)
+ {
+ case dma_eth:
+ if ((dmanr != NETWORK_TX_DMA_NBR) &&
+ (dmanr != NETWORK_RX_DMA_NBR)) {
+ printk(KERN_CRIT "Invalid DMA channel for eth\n");
+ goto bail;
+ }
+ break;
+ case dma_ser0:
+ if (dmanr == SER0_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma6, serial0);
+ } else if (dmanr == SER0_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma7, serial0);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ser0\n");
+ goto bail;
+ }
+ break;
+ case dma_ser1:
+ if (dmanr == SER1_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma8, serial1);
+ } else if (dmanr == SER1_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma9, serial1);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ser1\n");
+ goto bail;
+ }
+ break;
+ case dma_ser2:
+ if (dmanr == SER2_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma2, serial2);
+ } else if (dmanr == SER2_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma3, serial2);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ser2\n");
+ goto bail;
+ }
+ break;
+ case dma_ser3:
+ if (dmanr == SER3_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma4, serial3);
+ } else if (dmanr == SER3_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma5, serial3);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ser3\n");
+ goto bail;
+ }
+ break;
+ case dma_ata:
+ if (dmanr == ATA_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma2, ata);
+ } else if (dmanr == ATA_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma3, ata);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ata\n");
+ goto bail;
+ }
+ break;
+ case dma_ext0:
+ if (dmanr == EXTDMA0_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma4, extdma0);
+ } else if (dmanr == EXTDMA0_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma5, extdma0);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ext0\n");
+ goto bail;
+ }
+ break;
+ case dma_ext1:
+ if (dmanr == EXTDMA1_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma6, extdma1);
+ } else if (dmanr == EXTDMA1_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma7, extdma1);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for ext1\n");
+ goto bail;
+ }
+ break;
+ case dma_int6:
+ if (dmanr == MEM2MEM_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma7, intdma6);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for int6\n");
+ goto bail;
+ }
+ break;
+ case dma_int7:
+ if (dmanr == MEM2MEM_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma6, intdma7);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for int7\n");
+ goto bail;
+ }
+ break;
+ case dma_usb:
+ if (dmanr == USB_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma8, usb);
+ } else if (dmanr == USB_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma9, usb);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for usb\n");
+ goto bail;
+ }
+ break;
+ case dma_scsi0:
+ if (dmanr == SCSI0_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma2, scsi0);
+ } else if (dmanr == SCSI0_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma3, scsi0);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for scsi0\n");
+ goto bail;
+ }
+ break;
+ case dma_scsi1:
+ if (dmanr == SCSI1_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma4, scsi1);
+ } else if (dmanr == SCSI1_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma5, scsi1);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for scsi1\n");
+ goto bail;
+ }
+ break;
+ case dma_par0:
+ if (dmanr == PAR0_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma2, par0);
+ } else if (dmanr == PAR0_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma3, par0);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for par0\n");
+ goto bail;
+ }
+ break;
+ case dma_par1:
+ if (dmanr == PAR1_TX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma4, par1);
+ } else if (dmanr == PAR1_RX_DMA_NBR) {
+ SETS(gens, R_GEN_CONFIG, dma5, par1);
+ } else {
+ printk(KERN_CRIT "Invalid DMA channel for par1\n");
+ goto bail;
+ }
+ break;
+ default:
+ printk(KERN_CRIT "Invalid DMA owner.\n");
+ goto bail;
+ }
+
+ used_dma_channels[dmanr] = 1;
+ used_dma_channels_users[dmanr] = device_id;
+
+ {
+ volatile int i;
+ genconfig_shadow = gens;
+ *R_GEN_CONFIG = genconfig_shadow;
+ /* Wait 12 cycles before doing any DMA command */
+ for(i = 6; i > 0; i--)
+ nop();
+ }
+ fail = 0;
+ bail:
+ local_irq_restore(flags);
+ return fail;
+}
+
+void cris_free_dma(unsigned int dmanr, const char * device_id)
+{
+ unsigned long flags;
+ if (dmanr >= MAX_DMA_CHANNELS) {
+ printk(KERN_CRIT "cris_free_dma: invalid DMA channel %u\n", dmanr);
+ return;
+ }
+
+ local_irq_save(flags);
+ if (!used_dma_channels[dmanr]) {
+ printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated\n", dmanr);
+ } else if (device_id != used_dma_channels_users[dmanr]) {
+ printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated by device\n", dmanr);
+ } else {
+ switch(dmanr)
+ {
+ case 0:
+ *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH0_CMD, cmd, *R_DMA_CH0_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH0_CMD, cmd, reset));
+ break;
+ case 1:
+ *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH1_CMD, cmd, *R_DMA_CH1_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH1_CMD, cmd, reset));
+ break;
+ case 2:
+ *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH2_CMD, cmd, *R_DMA_CH2_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH2_CMD, cmd, reset));
+ break;
+ case 3:
+ *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH3_CMD, cmd, *R_DMA_CH3_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH3_CMD, cmd, reset));
+ break;
+ case 4:
+ *R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH4_CMD, cmd, *R_DMA_CH4_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH4_CMD, cmd, reset));
+ break;
+ case 5:
+ *R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH5_CMD, cmd, *R_DMA_CH5_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH5_CMD, cmd, reset));
+ break;
+ case 6:
+ *R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *R_DMA_CH6_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+ break;
+ case 7:
+ *R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH7_CMD, cmd, *R_DMA_CH7_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH7_CMD, cmd, reset));
+ break;
+ case 8:
+ *R_DMA_CH8_CMD = IO_STATE(R_DMA_CH8_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH8_CMD, cmd, *R_DMA_CH8_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH8_CMD, cmd, reset));
+ break;
+ case 9:
+ *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, reset);
+ while (IO_EXTRACT(R_DMA_CH9_CMD, cmd, *R_DMA_CH9_CMD) ==
+ IO_STATE_VALUE(R_DMA_CH9_CMD, cmd, reset));
+ break;
+ }
+ used_dma_channels[dmanr] = 0;
+ }
+ local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(cris_request_dma);
+EXPORT_SYMBOL(cris_free_dma);
diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S
new file mode 100644
index 000000000..81570fcd0
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/entry.S
@@ -0,0 +1,969 @@
+/*
+ * linux/arch/cris/entry.S
+ *
+ * Copyright (C) 2000, 2001, 2002 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ */
+
+/*
+ * 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/linkage.h>
+#include <linux/sys.h>
+#include <asm/unistd.h>
+#include <arch/sv_addr_ag.h>
+#include <asm/errno.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.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 ret_from_kernel_thread
+ .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:
+ di
+ ; 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
+ jsr preempt_schedule_irq
+ ; 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_kernel_thread:
+ jsr schedule_tail
+ move.d $r2, $r10 ; argument is here
+ jsr $r1 ; call the payload
+ moveq 0, $r9 ; no syscall restarts, TYVM...
+ ba ret_from_sys_call
+
+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
+ move.d $sp, $r11 ; the regs param
+ move.d $r1, $r12 ; 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 24, $r1 ; Get PGD index (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 PGD (bit 13-23)
+ 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:
+ ;; 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
+
+ ;; If there is a glitch on the NMI pin shorter than ~100ns
+ ;; (i.e. non-active by the time we get here) then the nmi_pin bit
+ ;; in R_IRQ_MASK0_RD will already be cleared. The watchdog_nmi bit
+ ;; is cleared by us however (when feeding the watchdog), which is why
+ ;; we use that bit to determine what brought us here.
+
+ move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog?
+ and.d (1<<30), $r1
+ bne wdog
+ move.d $sp, $r10
+ jsr handle_nmi
+ setf m ; Enable NMI again
+ ba _Rexit ; Return the standard way
+ nop
+wdog:
+#if defined(CONFIG_ETRAX_WATCHDOG)
+;; 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 */
+
+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
+
+ move.d $sp, $r10
+ jsr do_multiple_IRQ
+
+ 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 sys_old_readdir
+ .long sys_old_mmap /* 90 */
+ .long sys_munmap
+ .long sys_truncate
+ .long sys_ftruncate
+ .long sys_fchmod
+ .long sys_fchown16 /* 95 */
+ .long sys_getpriority
+ .long sys_setpriority
+ .long sys_ni_syscall /* old profil syscall holder */
+ .long sys_statfs
+ .long sys_fstatfs /* 100 */
+ .long sys_ni_syscall /* sys_ioperm in i386 */
+ .long sys_socketcall
+ .long sys_syslog
+ .long sys_setitimer
+ .long sys_getitimer /* 105 */
+ .long sys_newstat
+ .long sys_newlstat
+ .long sys_newfstat
+ .long sys_ni_syscall /* old sys_uname holder */
+ .long sys_ni_syscall /* 110 */ /* sys_iopl in i386 */
+ .long sys_vhangup
+ .long sys_ni_syscall /* old "idle" system call */
+ .long sys_ni_syscall /* vm86old in i386 */
+ .long sys_wait4
+ .long sys_swapoff /* 115 */
+ .long sys_sysinfo
+ .long sys_ipc
+ .long sys_fsync
+ .long sys_sigreturn
+ .long sys_clone /* 120 */
+ .long sys_setdomainname
+ .long sys_newuname
+ .long sys_ni_syscall /* sys_modify_ldt */
+ .long sys_adjtimex
+ .long sys_mprotect /* 125 */
+ .long sys_sigprocmask
+ .long sys_ni_syscall /* old "create_module" */
+ .long sys_init_module
+ .long sys_delete_module
+ .long sys_ni_syscall /* 130: old "get_kernel_syms" */
+ .long sys_quotactl
+ .long sys_getpgid
+ .long sys_fchdir
+ .long sys_bdflush
+ .long sys_sysfs /* 135 */
+ .long sys_personality
+ .long sys_ni_syscall /* for afs_syscall */
+ .long sys_setfsuid16
+ .long sys_setfsgid16
+ .long sys_llseek /* 140 */
+ .long sys_getdents
+ .long sys_select
+ .long sys_flock
+ .long sys_msync
+ .long sys_readv /* 145 */
+ .long sys_writev
+ .long sys_getsid
+ .long sys_fdatasync
+ .long sys_sysctl
+ .long sys_mlock /* 150 */
+ .long sys_munlock
+ .long sys_mlockall
+ .long sys_munlockall
+ .long sys_sched_setparam
+ .long sys_sched_getparam /* 155 */
+ .long sys_sched_setscheduler
+ .long sys_sched_getscheduler
+ .long sys_sched_yield
+ .long sys_sched_get_priority_max
+ .long sys_sched_get_priority_min /* 160 */
+ .long sys_sched_rr_get_interval
+ .long sys_nanosleep
+ .long sys_mremap
+ .long sys_setresuid16
+ .long sys_getresuid16 /* 165 */
+ .long sys_ni_syscall /* sys_vm86 */
+ .long sys_ni_syscall /* Old sys_query_module */
+ .long sys_poll
+ .long sys_ni_syscall /* old nfsservctl */
+ .long sys_setresgid16 /* 170 */
+ .long sys_getresgid16
+ .long sys_prctl
+ .long sys_rt_sigreturn
+ .long sys_rt_sigaction
+ .long sys_rt_sigprocmask /* 175 */
+ .long sys_rt_sigpending
+ .long sys_rt_sigtimedwait
+ .long sys_rt_sigqueueinfo
+ .long sys_rt_sigsuspend
+ .long sys_pread64 /* 180 */
+ .long sys_pwrite64
+ .long sys_chown16
+ .long sys_getcwd
+ .long sys_capget
+ .long sys_capset /* 185 */
+ .long sys_sigaltstack
+ .long sys_sendfile
+ .long sys_ni_syscall /* streams1 */
+ .long sys_ni_syscall /* streams2 */
+ .long sys_vfork /* 190 */
+ .long sys_getrlimit
+ .long sys_mmap2 /* mmap_pgoff */
+ .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
+ .long sys_waitid
+ .long sys_ni_syscall /* 285 */ /* available */
+ .long sys_add_key
+ .long sys_request_key
+ .long sys_keyctl
+ .long sys_ioprio_set
+ .long sys_ioprio_get /* 290 */
+ .long sys_inotify_init
+ .long sys_inotify_add_watch
+ .long sys_inotify_rm_watch
+ .long sys_migrate_pages
+ .long sys_openat /* 295 */
+ .long sys_mkdirat
+ .long sys_mknodat
+ .long sys_fchownat
+ .long sys_futimesat
+ .long sys_fstatat64 /* 300 */
+ .long sys_unlinkat
+ .long sys_renameat
+ .long sys_linkat
+ .long sys_symlinkat
+ .long sys_readlinkat /* 305 */
+ .long sys_fchmodat
+ .long sys_faccessat
+ .long sys_pselect6
+ .long sys_ppoll
+ .long sys_unshare /* 310 */
+ .long sys_set_robust_list
+ .long sys_get_robust_list
+ .long sys_splice
+ .long sys_sync_file_range
+ .long sys_tee /* 315 */
+ .long sys_vmsplice
+ .long sys_move_pages
+ .long sys_getcpu
+ .long sys_epoll_pwait
+ .long sys_utimensat /* 320 */
+ .long sys_signalfd
+ .long sys_timerfd_create
+ .long sys_eventfd
+ .long sys_fallocate
+ .long sys_timerfd_settime /* 325 */
+ .long sys_timerfd_gettime
+ .long sys_signalfd4
+ .long sys_eventfd2
+ .long sys_epoll_create1
+ .long sys_dup3 /* 330 */
+ .long sys_pipe2
+ .long sys_inotify_init1
+ .long sys_preadv
+ .long sys_pwritev
+ .long sys_setns /* 335 */
+ .long sys_name_to_handle_at
+ .long sys_open_by_handle_at
+ .long sys_rt_tgsigqueueinfo
+ .long sys_perf_event_open
+ .long sys_recvmmsg /* 340 */
+ .long sys_accept4
+ .long sys_fanotify_init
+ .long sys_fanotify_mark
+ .long sys_prlimit64
+ .long sys_clock_adjtime /* 345 */
+ .long sys_syncfs
+ .long sys_sendmmsg
+ .long sys_process_vm_readv
+ .long sys_process_vm_writev
+ .long sys_kcmp /* 350 */
+ .long sys_finit_module
+
+ /*
+ * NOTE!! This doesn't have to be exact - we just have
+ * to make sure we have _enough_ of the "sys_ni_syscall"
+ * entries. Don't panic if you notice that this hasn't
+ * been shrunk every time we add a new system call.
+ */
+
+ .rept NR_syscalls-(.-sys_call_table)/4
+ .long sys_ni_syscall
+ .endr
+
diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c
new file mode 100644
index 000000000..e9298739d
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/fasttimer.c
@@ -0,0 +1,834 @@
+/*
+ * linux/arch/cris/kernel/fasttimer.c
+ *
+ * Fast timers for ETRAX100/ETRAX100LX
+ *
+ * Copyright (C) 2000-2007 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 <arch/svinto.h>
+#include <asm/fasttimer.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+
+#define DEBUG_LOG_INCLUDED
+#define FAST_TIMER_LOG
+/* #define FAST_TIMER_TEST */
+
+#define FAST_TIMER_SANITY_CHECKS
+
+#ifdef FAST_TIMER_SANITY_CHECKS
+static int sanity_failed;
+#endif
+
+#define D1(x)
+#define D2(x)
+#define DP(x)
+
+static unsigned int fast_timer_running;
+static unsigned int fast_timers_added;
+static unsigned int fast_timers_started;
+static unsigned int fast_timers_expired;
+static unsigned int fast_timers_deleted;
+static unsigned int fast_timer_is_init;
+static unsigned int fast_timer_ints;
+
+struct fast_timer *fast_timer_list = NULL;
+
+#ifdef DEBUG_LOG_INCLUDED
+#define DEBUG_LOG_MAX 128
+static const char * debug_log_string[DEBUG_LOG_MAX];
+static unsigned long debug_log_value[DEBUG_LOG_MAX];
+static unsigned int debug_log_cnt;
+static unsigned int debug_log_cnt_wrapped;
+
+#define DEBUG_LOG(string, value) \
+{ \
+ unsigned long log_flags; \
+ local_irq_save(log_flags); \
+ debug_log_string[debug_log_cnt] = (string); \
+ debug_log_value[debug_log_cnt] = (unsigned long)(value); \
+ if (++debug_log_cnt >= DEBUG_LOG_MAX) \
+ { \
+ debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
+ debug_log_cnt_wrapped = 1; \
+ } \
+ local_irq_restore(log_flags); \
+}
+#else
+#define DEBUG_LOG(string, value)
+#endif
+
+
+/* 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 */
+inline void do_gettimeofday_fast(struct fasttime_t *tv)
+{
+ tv->tv_jiff = jiffies;
+ tv->tv_usec = GET_JIFFIES_USEC();
+}
+
+inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
+{
+ /* Compare jiffies. Takes care of wrapping */
+ if (time_before(t0->tv_jiff, t1->tv_jiff))
+ return -1;
+ else if (time_after(t0->tv_jiff, t1->tv_jiff))
+ return 1;
+
+ /* Compare us */
+ if (t0->tv_usec < t1->tv_usec)
+ return -1;
+ else if (t0->tv_usec > t1->tv_usec)
+ return 1;
+ return 0;
+}
+
+inline void 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(KERN_DEBUG "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));
+
+ local_irq_save(flags);
+
+ do_gettimeofday_fast(&t->tv_set);
+ tmp = fast_timer_list;
+
+#ifdef FAST_TIMER_SANITY_CHECKS
+ /* Check so this is not in the list already... */
+ while (tmp != NULL) {
+ if (tmp == t) {
+ printk(KERN_WARNING "timer name: %s data: "
+ "0x%08lX already in list!\n", name, data);
+ sanity_failed++;
+ goto done;
+ } else
+ tmp = tmp->next;
+ }
+ tmp = fast_timer_list;
+#endif
+
+ t->delay_us = delay_us;
+ t->function = function;
+ t->data = data;
+ t->name = name;
+
+ t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
+ t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
+ if (t->tv_expires.tv_usec > 1000000)
+ {
+ t->tv_expires.tv_usec -= 1000000;
+ t->tv_expires.tv_jiff += HZ;
+ }
+#ifdef FAST_TIMER_LOG
+ timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
+#endif
+ fast_timers_added++;
+
+ /* Check if this should timeout before anything else */
+ if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0)
+ {
+ /* Put first in list and modify the timer value */
+ t->prev = NULL;
+ t->next = fast_timer_list;
+ if (fast_timer_list)
+ {
+ fast_timer_list->prev = t;
+ }
+ fast_timer_list = t;
+#ifdef FAST_TIMER_LOG
+ timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
+#endif
+ start_timer1(delay_us);
+ } else {
+ /* Put in correct place in list */
+ while (tmp->next && fasttime_cmp(&t->tv_expires,
+ &tmp->next->tv_expires) > 0)
+ {
+ tmp = tmp->next;
+ }
+ /* Insert t after tmp */
+ t->prev = tmp;
+ t->next = tmp->next;
+ if (tmp->next)
+ {
+ tmp->next->prev = t;
+ }
+ tmp->next = t;
+ }
+
+ D2(printk("start_one_shot_timer: %d us done\n", delay_us));
+
+done:
+ local_irq_restore(flags);
+} /* start_one_shot_timer */
+
+static inline int fast_timer_pending (const struct fast_timer * t)
+{
+ return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
+}
+
+static inline int detach_fast_timer (struct fast_timer *t)
+{
+ struct fast_timer *next, *prev;
+ if (!fast_timer_pending(t))
+ return 0;
+ next = t->next;
+ prev = t->prev;
+ if (next)
+ next->prev = prev;
+ if (prev)
+ prev->next = next;
+ else
+ fast_timer_list = next;
+ fast_timers_deleted++;
+ return 1;
+}
+
+int del_fast_timer(struct fast_timer * t)
+{
+ unsigned long flags;
+ int ret;
+
+ local_irq_save(flags);
+ ret = detach_fast_timer(t);
+ t->next = t->prev = NULL;
+ local_irq_restore(flags);
+ return ret;
+} /* del_fast_timer */
+
+
+/* Interrupt routines or functions called in interrupt context */
+
+/* Timer 1 interrupt handler */
+
+static irqreturn_t
+timer1_handler(int irq, void *dev_id)
+{
+ struct fast_timer *t;
+ unsigned long flags;
+
+ /* We keep interrupts disabled not only when we modify the
+ * fast timer list, but any time we hold a reference to a
+ * timer in the list, since del_fast_timer may be called
+ * from (another) interrupt context. Thus, the only time
+ * when interrupts are enabled is when calling the timer
+ * callback function.
+ */
+ local_irq_save(flags);
+
+ /* Clear timer1 irq */
+ *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++;
+
+ t = fast_timer_list;
+ while (t)
+ {
+ struct fasttime_t tv;
+ fast_timer_function_type *f;
+ unsigned long d;
+
+ /* Has it really expired? */
+ do_gettimeofday_fast(&tv);
+ D1(printk(KERN_DEBUG "t: %is %06ius\n",
+ tv.tv_jiff, tv.tv_usec));
+
+ if (fasttime_cmp(&t->tv_expires, &tv) <= 0)
+ {
+ /* Yes it has expired */
+#ifdef FAST_TIMER_LOG
+ timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t;
+#endif
+ fast_timers_expired++;
+
+ /* Remove this timer before call, since it may reuse the timer */
+ if (t->prev)
+ {
+ t->prev->next = t->next;
+ }
+ else
+ {
+ fast_timer_list = t->next;
+ }
+ if (t->next)
+ {
+ t->next->prev = t->prev;
+ }
+ t->prev = NULL;
+ t->next = NULL;
+
+ /* Save function callback data before enabling
+ * interrupts, since the timer may be removed and
+ * we don't know how it was allocated
+ * (e.g. ->function and ->data may become overwritten
+ * after deletion if the timer was stack-allocated).
+ */
+ f = t->function;
+ d = t->data;
+
+ if (f != NULL) {
+ /* Run callback with interrupts enabled. */
+ local_irq_restore(flags);
+ f(d);
+ local_irq_save(flags);
+ } 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"));
+ }
+
+ if ((t = fast_timer_list) != NULL)
+ {
+ /* Start next timer.. */
+ long us = 0;
+ struct fasttime_t tv;
+
+ do_gettimeofday_fast(&tv);
+
+ /* time_after_eq takes care of wrapping */
+ if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
+ us = ((t->tv_expires.tv_jiff - tv.tv_jiff) *
+ 1000000 / HZ + t->tv_expires.tv_usec -
+ tv.tv_usec);
+
+ if (us > 0)
+ {
+ if (!fast_timer_running)
+ {
+#ifdef FAST_TIMER_LOG
+ timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
+#endif
+ start_timer1(us);
+ }
+ break;
+ }
+ else
+ {
+ /* Timer already expired, let's handle it better late than never.
+ * The normal loop handles it
+ */
+ D1(printk("e! %d\n", us));
+ }
+ }
+ }
+
+ local_irq_restore(flags);
+
+ if (!t)
+ {
+ D1(printk("t1 stop!\n"));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void wake_up_func(unsigned long data)
+{
+ wait_queue_head_t *sleep_wait_p = (wait_queue_head_t *)data;
+ wake_up(sleep_wait_p);
+}
+
+
+/* Useful API */
+
+void schedule_usleep(unsigned long us)
+{
+ struct fast_timer t;
+ wait_queue_head_t sleep_wait;
+ init_waitqueue_head(&sleep_wait);
+
+ D1(printk("schedule_usleep(%d)\n", us));
+ start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
+ "usleep");
+ /* Uninterruptible sleep on the fast timer. (The condition is somewhat
+ * redundant since the timer is what wakes us up.) */
+ wait_event(sleep_wait, !fast_timer_pending(&t));
+
+ D1(printk("done schedule_usleep(%d)\n", us));
+}
+
+#ifdef CONFIG_PROC_FS
+/* This value is very much based on testing */
+#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
+
+static int proc_fasttimer_show(struct seq_file *m, void *v)
+{
+ unsigned long flags;
+ int i = 0;
+ int num_to_show;
+ struct fasttime_t tv;
+ struct fast_timer *t, *nextt;
+
+ do_gettimeofday_fast(&tv);
+
+ seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
+ seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
+ seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
+ seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
+ seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
+ seq_printf(m, "Fast timer running: %s\n",
+ fast_timer_running ? "yes" : "no");
+ seq_printf(m, "Current time: %lu.%06lu\n",
+ (unsigned long)tv.tv_jiff,
+ (unsigned long)tv.tv_usec);
+#ifdef FAST_TIMER_SANITY_CHECKS
+ seq_printf(m, "Sanity failed: %i\n", sanity_failed);
+#endif
+ seq_putc(m, '\n');
+
+#ifdef DEBUG_LOG_INCLUDED
+ {
+ int end_i = debug_log_cnt;
+ i = 0;
+
+ if (debug_log_cnt_wrapped)
+ i = debug_log_cnt;
+
+ while (i != end_i || debug_log_cnt_wrapped) {
+ seq_printf(m, debug_log_string[i], debug_log_value[i]);
+ if (seq_has_overflowed(m))
+ return 0;
+ i = (i+1) % DEBUG_LOG_MAX;
+ }
+ }
+ seq_putc(m, '\n');
+#endif
+
+ num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers started: %i\n", fast_timers_started);
+ for (i = 0; i < num_to_show; i++) {
+ int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
+
+#if 1 //ndef FAST_TIMER_LOG
+ seq_printf(m, "div: %i 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];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_jiff,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_jiff,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data);
+ if (seq_has_overflowed(m))
+ return 0;
+#endif
+ }
+ seq_putc(m, '\n');
+
+#ifdef FAST_TIMER_LOG
+ num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers added: %i\n", fast_timers_added);
+ for (i = 0; i < num_to_show; i++) {
+ t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_jiff,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_jiff,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data);
+ if (seq_has_overflowed(m))
+ return 0;
+ }
+ seq_putc(m, '\n');
+
+ num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
+ NUM_TIMER_STATS);
+ seq_printf(m, "Timers expired: %i\n", fast_timers_expired);
+ for (i = 0; i < num_to_show; i++) {
+ t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_jiff,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_jiff,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data);
+ if (seq_has_overflowed(m))
+ return 0;
+ }
+ seq_putc(m, '\n');
+#endif
+
+ seq_puts(m, "Active timers:\n");
+ local_irq_save(flags);
+ t = fast_timer_list;
+ while (t) {
+ nextt = t->next;
+ local_irq_restore(flags);
+ seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
+ t->name,
+ (unsigned long)t->tv_set.tv_jiff,
+ (unsigned long)t->tv_set.tv_usec,
+ (unsigned long)t->tv_expires.tv_jiff,
+ (unsigned long)t->tv_expires.tv_usec,
+ t->delay_us,
+ t->data);
+ if (seq_has_overflowed(m))
+ return 0;
+ local_irq_save(flags);
+ if (t->next != nextt)
+ printk(KERN_WARNING "timer removed!\n");
+ t = nextt;
+ }
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int proc_fasttimer_open(struct inode *inode, struct file *file)
+{
+ return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
+}
+
+static const struct file_operations proc_fasttimer_fops = {
+ .open = proc_fasttimer_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* PROC_FS */
+
+#ifdef FAST_TIMER_TEST
+static volatile unsigned long i = 0;
+static volatile int num_test_timeout = 0;
+static struct fast_timer tr[10];
+static int exp_num[10];
+
+static struct fasttime_t tv_exp[100];
+
+static void test_timeout(unsigned long data)
+{
+ do_gettimeofday_fast(&tv_exp[data]);
+ exp_num[data] = num_test_timeout;
+
+ num_test_timeout++;
+}
+
+static void test_timeout1(unsigned long data)
+{
+ do_gettimeofday_fast(&tv_exp[data]);
+ exp_num[data] = num_test_timeout;
+ if (data < 7)
+ {
+ start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1");
+ i++;
+ }
+ num_test_timeout++;
+}
+
+DP(
+static char buf0[2000];
+static char buf1[2000];
+static char buf2[2000];
+static char buf3[2000];
+static char buf4[2000];
+);
+
+static char buf5[6000];
+static int j_u[1000];
+
+static void fast_timer_test(void)
+{
+ int prev_num;
+ int j;
+
+ struct fasttime_t tv, tv0, tv1, tv2;
+
+ printk("fast_timer_test() start\n");
+ do_gettimeofday_fast(&tv);
+
+ for (j = 0; j < 1000; j++)
+ {
+ j_u[j] = GET_JIFFIES_USEC();
+ }
+ for (j = 0; j < 100; j++)
+ {
+ do_gettimeofday_fast(&tv_exp[j]);
+ }
+ printk(KERN_DEBUG "fast_timer_test() %is %06i\n",
+ tv.tv_jiff, tv.tv_usec);
+
+ for (j = 0; j < 1000; j++)
+ {
+ printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]);
+ j += 4;
+ }
+ for (j = 0; j < 100; j++)
+ {
+ printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n",
+ tv_exp[j].tv_jiff, tv_exp[j].tv_usec,
+ tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec,
+ tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec,
+ tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec,
+ tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec);
+ j += 4;
+ }
+ do_gettimeofday_fast(&tv0);
+ start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0");
+ DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1");
+ DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2");
+ DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3");
+ DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0));
+ i++;
+ start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx");
+ DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0));
+ i++;
+ do_gettimeofday_fast(&tv1);
+
+ proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+
+ prev_num = num_test_timeout;
+ while (num_test_timeout < i)
+ {
+ if (num_test_timeout != prev_num)
+ {
+ prev_num = num_test_timeout;
+ }
+ }
+ do_gettimeofday_fast(&tv2);
+ printk(KERN_DEBUG "Timers started %is %06i\n",
+ tv0.tv_jiff, tv0.tv_usec);
+ printk(KERN_DEBUG "Timers started at %is %06i\n",
+ tv1.tv_jiff, tv1.tv_usec);
+ printk(KERN_DEBUG "Timers done %is %06i\n",
+ tv2.tv_jiff, tv2.tv_usec);
+ DP(printk("buf0:\n");
+ printk(buf0);
+ printk("buf1:\n");
+ printk(buf1);
+ printk("buf2:\n");
+ printk(buf2);
+ printk("buf3:\n");
+ printk(buf3);
+ printk("buf4:\n");
+ printk(buf4);
+ );
+ printk("buf5:\n");
+ printk(buf5);
+
+ printk("timers set:\n");
+ for(j = 0; j<i; j++)
+ {
+ struct fast_timer *t = &tr[j];
+ printk("%-10s set: %6is %06ius exp: %6is %06ius "
+ "data: 0x%08X func: 0x%08X\n",
+ t->name,
+ t->tv_set.tv_jiff,
+ t->tv_set.tv_usec,
+ t->tv_expires.tv_jiff,
+ t->tv_expires.tv_usec,
+ t->data,
+ t->function
+ );
+
+ printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n",
+ t->delay_us,
+ tv_exp[j].tv_jiff,
+ tv_exp[j].tv_usec,
+ exp_num[j],
+ (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) *
+ 1000000 + tv_exp[j].tv_usec -
+ t->tv_expires.tv_usec);
+ }
+ proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+ printk("buf5 after all done:\n");
+ printk(buf5);
+ printk("fast_timer_test() done\n");
+}
+#endif
+
+
+int fast_timer_init(void)
+{
+ /* For some reason, request_irq() hangs when called froom time_init() */
+ if (!fast_timer_is_init)
+ {
+#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
+ proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
+#endif /* PROC_FS */
+ if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 0,
+ "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
+ }
+ return 0;
+}
+__initcall(fast_timer_init);
diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S
new file mode 100644
index 000000000..4a146e174
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/head.S
@@ -0,0 +1,723 @@
+/*
+ * Head of the kernel - alter with care
+ *
+ * Copyright (C) 2000, 2001, 2010 Axis Communications AB
+ *
+ */
+
+#define ASSEMBLER_MACROS_ONLY
+/* The IO_* macros use the ## token concatenation operator, so
+ -traditional must not be used when assembling this file. */
+#include <arch/sv_addr_ag.h>
+
+#define CRAMFS_MAGIC 0x28cd3d45
+#define RAM_INIT_MAGIC 0x56902387
+#define COMMAND_LINE_MAGIC 0x87109563
+
+#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.
+ 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]
+
+ ;; 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 __init_end, $r0; the image will be after the end of init
+ 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:
+
+ ;; Check if kernel command line is supplied
+ cmp.d COMMAND_LINE_MAGIC, $r10
+ bne no_command_line
+ nop
+
+ move.d 256, $r13
+ move.d cris_command_line, $r10
+ or.d 0x80000000, $r11 ; Make it virtual
+1:
+ move.b [$r11+], $r12
+ move.b $r12, [$r10+]
+ subq 1, $r13
+ bne 1b
+ nop
+
+no_command_line:
+
+ ;; 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
+
+ ;; Select or disable serial port 2
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+ or.d IO_STATE (R_GEN_CONFIG, ser2, select),$r0
+#else
+ or.d IO_STATE (R_GEN_CONFIG, ser2, disable),$r0
+#endif
+
+ ;; Init interfaces (disable them).
+ or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \
+ | IO_STATE (R_GEN_CONFIG, ata, disable) \
+ | IO_STATE (R_GEN_CONFIG, par0, disable) \
+ | IO_STATE (R_GEN_CONFIG, mio, disable) \
+ | IO_STATE (R_GEN_CONFIG, scsi1, disable) \
+ | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \
+ | IO_STATE (R_GEN_CONFIG, par1, disable) \
+ | IO_STATE (R_GEN_CONFIG, ser3, disable) \
+ | IO_STATE (R_GEN_CONFIG, mio_w, disable) \
+ | IO_STATE (R_GEN_CONFIG, usb1, disable) \
+ | IO_STATE (R_GEN_CONFIG, usb2, disable) \
+ | IO_STATE (R_GEN_CONFIG, par_w, disable),$r0
+
+ ;; Init DMA channel muxing (set to unused clients).
+ or.d IO_STATE (R_GEN_CONFIG, dma2, ata) \
+ | IO_STATE (R_GEN_CONFIG, dma3, ata) \
+ | IO_STATE (R_GEN_CONFIG, dma4, scsi1) \
+ | IO_STATE (R_GEN_CONFIG, dma5, scsi1) \
+ | IO_STATE (R_GEN_CONFIG, dma6, unused) \
+ | IO_STATE (R_GEN_CONFIG, dma7, unused) \
+ | IO_STATE (R_GEN_CONFIG, dma8, usb) \
+ | IO_STATE (R_GEN_CONFIG, dma9, usb),$r0
+
+
+#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
+
+ 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_PORT2
+ ;; setup the serial port 2 at 115200 baud for debug purposes
+
+ moveq IO_STATE (R_SERIAL2_XOFF, tx_stop, enable) \
+ | IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable) \
+ | IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0
+ move.d $r0,[R_SERIAL2_XOFF]
+
+ ; 115.2kbaud for both transmit and receive
+ move.b IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz) \
+ | IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0
+ move.b $r0,[R_SERIAL2_BAUD]
+
+ ; Set up and enable the serial2 receiver.
+ move.b IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rts_, active) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable) \
+ | IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0
+ move.b $r0,[R_SERIAL2_REC_CTRL]
+
+ ; Set up and enable the serial2 transmitter.
+ move.b IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable) \
+ | IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0
+ move.b $r0,[R_SERIAL2_TR_CTRL]
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+ ;; setup the serial port 3 at 115200 baud for debug purposes
+
+ 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
+
+ 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/io_interface_mux.c b/arch/cris/arch-v10/kernel/io_interface_mux.c
new file mode 100644
index 000000000..ad64cd1c8
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/io_interface_mux.c
@@ -0,0 +1,1182 @@
+/* IO interface mux allocator for ETRAX100LX.
+ * Copyright 2004-2007, Axis Communications AB
+ */
+
+
+/* C.f. ETRAX100LX Designer's Reference chapter 19.9 */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <arch/svinto.h>
+#include <asm/io.h>
+#include <arch/io_interface_mux.h>
+#include <arch/system.h>
+
+
+#define DBG(s)
+
+/* Macro to access ETRAX 100 registers */
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+ IO_STATE_(reg##_, field##_, _##val)
+
+enum io_if_group {
+ group_a = (1<<0),
+ group_b = (1<<1),
+ group_c = (1<<2),
+ group_d = (1<<3),
+ group_e = (1<<4),
+ group_f = (1<<5)
+};
+
+struct watcher
+{
+ void (*notify)(const unsigned int gpio_in_available,
+ const unsigned int gpio_out_available,
+ const unsigned char pa_available,
+ const unsigned char pb_available);
+ struct watcher *next;
+};
+
+
+struct if_group
+{
+ enum io_if_group group;
+ /* name - the name of the group 'A' to 'F' */
+ char *name;
+ /* used - a bit mask of all pins in the group in the order listed
+ * in the tables in 19.9.1 to 19.9.6. Note that no
+ * distinction is made between in, out and in/out pins. */
+ unsigned int used;
+};
+
+
+struct interface
+{
+ enum cris_io_interface ioif;
+ /* name - the name of the interface */
+ char *name;
+ /* groups - OR'ed together io_if_group flags describing what pin groups
+ * the interface uses pins in. */
+ unsigned char groups;
+ /* used - set when the interface is allocated. */
+ unsigned char used;
+ char *owner;
+ /* group_a through group_f - bit masks describing what pins in the
+ * pin groups the interface uses. */
+ unsigned int group_a;
+ unsigned int group_b;
+ unsigned int group_c;
+ unsigned int group_d;
+ unsigned int group_e;
+ unsigned int group_f;
+
+ /* gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the
+ * GPIO ports the interface uses. This could be reconstucted using
+ * the group_X masks and a table of what pins the GPIO ports use,
+ * but that would be messy. */
+ unsigned int gpio_g_in;
+ unsigned int gpio_g_out;
+ unsigned char gpio_b;
+};
+
+static struct if_group if_groups[6] = {
+ {
+ .group = group_a,
+ .name = "A",
+ .used = 0,
+ },
+ {
+ .group = group_b,
+ .name = "B",
+ .used = 0,
+ },
+ {
+ .group = group_c,
+ .name = "C",
+ .used = 0,
+ },
+ {
+ .group = group_d,
+ .name = "D",
+ .used = 0,
+ },
+ {
+ .group = group_e,
+ .name = "E",
+ .used = 0,
+ },
+ {
+ .group = group_f,
+ .name = "F",
+ .used = 0,
+ }
+};
+
+/* The order in the array must match the order of enum
+ * cris_io_interface in io_interface_mux.h */
+static struct interface interfaces[] = {
+ /* Begin Non-multiplexed interfaces */
+ {
+ .ioif = if_eth,
+ .name = "ethernet",
+ .groups = 0,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0,
+ .gpio_g_out = 0,
+ .gpio_b = 0
+ },
+ {
+ .ioif = if_serial_0,
+ .name = "serial_0",
+ .groups = 0,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0,
+ .gpio_g_out = 0,
+ .gpio_b = 0
+ },
+ /* End Non-multiplexed interfaces */
+ {
+ .ioif = if_serial_1,
+ .name = "serial_1",
+ .groups = group_e,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0x0f,
+ .group_f = 0,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_serial_2,
+ .name = "serial_2",
+ .groups = group_b,
+
+ .group_a = 0,
+ .group_b = 0x0f,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x000000c0,
+ .gpio_g_out = 0x000000c0,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_serial_3,
+ .name = "serial_3",
+ .groups = group_c,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0x0f,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0xc0000000,
+ .gpio_g_out = 0xc0000000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_sync_serial_1,
+ .name = "sync_serial_1",
+ .groups = group_e | group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0x0f,
+ .group_f = 0x10,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0x10
+ },
+ {
+ .ioif = if_sync_serial_3,
+ .name = "sync_serial_3",
+ .groups = group_c | group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0x0f,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0x80,
+
+ .gpio_g_in = 0xc0000000,
+ .gpio_g_out = 0xc0000000,
+ .gpio_b = 0x80
+ },
+ {
+ .ioif = if_shared_ram,
+ .name = "shared_ram",
+ .groups = group_a,
+
+ .group_a = 0x7f8ff,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x0000ff3e,
+ .gpio_g_out = 0x0000ff38,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_shared_ram_w,
+ .name = "shared_ram_w",
+ .groups = group_a | group_d,
+
+ .group_a = 0x7f8ff,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0xff,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x00ffff3e,
+ .gpio_g_out = 0x00ffff38,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_par_0,
+ .name = "par_0",
+ .groups = group_a,
+
+ .group_a = 0x7fbff,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x0000ff3e,
+ .gpio_g_out = 0x0000ff3e,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_par_1,
+ .name = "par_1",
+ .groups = group_d,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0x7feff,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x3eff0000,
+ .gpio_g_out = 0x3eff0000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_par_w,
+ .name = "par_w",
+ .groups = group_a | group_d,
+
+ .group_a = 0x7fbff,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0xff,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x00ffff3e,
+ .gpio_g_out = 0x00ffff3e,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_scsi8_0,
+ .name = "scsi8_0",
+ .groups = group_a | group_b | group_f,
+
+ .group_a = 0x7ffff,
+ .group_b = 0x0f,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0x10,
+
+ .gpio_g_in = 0x0000ffff,
+ .gpio_g_out = 0x0000ffff,
+ .gpio_b = 0x10
+ },
+ {
+ .ioif = if_scsi8_1,
+ .name = "scsi8_1",
+ .groups = group_c | group_d | group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0x0f,
+ .group_d = 0x7ffff,
+ .group_e = 0,
+ .group_f = 0x80,
+
+ .gpio_g_in = 0xffff0000,
+ .gpio_g_out = 0xffff0000,
+ .gpio_b = 0x80
+ },
+ {
+ .ioif = if_scsi_w,
+ .name = "scsi_w",
+ .groups = group_a | group_b | group_d | group_f,
+
+ .group_a = 0x7ffff,
+ .group_b = 0x0f,
+ .group_c = 0,
+ .group_d = 0x601ff,
+ .group_e = 0,
+ .group_f = 0x90,
+
+ .gpio_g_in = 0x01ffffff,
+ .gpio_g_out = 0x07ffffff,
+ .gpio_b = 0x80
+ },
+ {
+ .ioif = if_ata,
+ .name = "ata",
+ .groups = group_a | group_b | group_c | group_d,
+
+ .group_a = 0x7ffff,
+ .group_b = 0x0f,
+ .group_c = 0x0f,
+ .group_d = 0x7cfff,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0xf9ffffff,
+ .gpio_g_out = 0xffffffff,
+ .gpio_b = 0x80
+ },
+ {
+ .ioif = if_csp,
+ .name = "csp",
+ .groups = group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0xfc,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0xfc
+ },
+ {
+ .ioif = if_i2c,
+ .name = "i2c",
+ .groups = group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0x03,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0x03
+ },
+ {
+ .ioif = if_usb_1,
+ .name = "usb_1",
+ .groups = group_e | group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0x0f,
+ .group_f = 0x2c,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0x2c
+ },
+ {
+ .ioif = if_usb_2,
+ .name = "usb_2",
+ .groups = group_d,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0x33e00,
+ .group_f = 0,
+
+ .gpio_g_in = 0x3e000000,
+ .gpio_g_out = 0x0c000000,
+ .gpio_b = 0x00
+ },
+ /* GPIO pins */
+ {
+ .ioif = if_gpio_grp_a,
+ .name = "gpio_a",
+ .groups = group_a,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x0000ff3f,
+ .gpio_g_out = 0x0000ff3f,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_gpio_grp_b,
+ .name = "gpio_b",
+ .groups = group_b,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x000000c0,
+ .gpio_g_out = 0x000000c0,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_gpio_grp_c,
+ .name = "gpio_c",
+ .groups = group_c,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0xc0000000,
+ .gpio_g_out = 0xc0000000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_gpio_grp_d,
+ .name = "gpio_d",
+ .groups = group_d,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x3fff0000,
+ .gpio_g_out = 0x3fff0000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_gpio_grp_e,
+ .name = "gpio_e",
+ .groups = group_e,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0x00
+ },
+ {
+ .ioif = if_gpio_grp_f,
+ .name = "gpio_f",
+ .groups = group_f,
+
+ .group_a = 0,
+ .group_b = 0,
+ .group_c = 0,
+ .group_d = 0,
+ .group_e = 0,
+ .group_f = 0,
+
+ .gpio_g_in = 0x00000000,
+ .gpio_g_out = 0x00000000,
+ .gpio_b = 0xff
+ }
+ /* Array end */
+};
+
+static struct watcher *watchers = NULL;
+
+/* The pins that are free to use in the GPIO ports. */
+static unsigned int gpio_in_pins = 0xffffffff;
+static unsigned int gpio_out_pins = 0xffffffff;
+static unsigned char gpio_pb_pins = 0xff;
+static unsigned char gpio_pa_pins = 0xff;
+
+/* Identifiers for the owners of the GPIO pins. */
+static enum cris_io_interface gpio_pa_owners[8];
+static enum cris_io_interface gpio_pb_owners[8];
+static enum cris_io_interface gpio_pg_owners[32];
+
+static int cris_io_interface_init(void);
+
+static unsigned char clear_group_from_set(const unsigned char groups, struct if_group *group)
+{
+ return (groups & ~group->group);
+}
+
+
+static struct if_group *get_group(const unsigned char groups)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(if_groups); i++) {
+ if (groups & if_groups[i].group) {
+ return &if_groups[i];
+ }
+ }
+ return NULL;
+}
+
+
+static void notify_watchers(void)
+{
+ struct watcher *w = watchers;
+
+ DBG(printk("io_interface_mux: notifying watchers\n"));
+
+ while (NULL != w) {
+ w->notify((const unsigned int)gpio_in_pins,
+ (const unsigned int)gpio_out_pins,
+ (const unsigned char)gpio_pa_pins,
+ (const unsigned char)gpio_pb_pins);
+ w = w->next;
+ }
+}
+
+
+int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id)
+{
+ int set_gen_config = 0;
+ int set_gen_config_ii = 0;
+ unsigned long int gens;
+ unsigned long int gens_ii;
+ struct if_group *grp;
+ unsigned char group_set;
+ unsigned long flags;
+ int res = 0;
+
+ (void)cris_io_interface_init();
+
+ DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id));
+
+ if ((ioif >= if_max_interfaces) || (ioif < 0)) {
+ printk(KERN_CRIT "cris_request_io_interface: Bad interface "
+ "%u submitted for %s\n",
+ ioif,
+ device_id);
+ return -EINVAL;
+ }
+
+ local_irq_save(flags);
+
+ if (interfaces[ioif].used) {
+ printk(KERN_CRIT "cris_io_interface: Cannot allocate interface "
+ "%s for %s, in use by %s\n",
+ interfaces[ioif].name,
+ device_id,
+ interfaces[ioif].owner);
+ res = -EBUSY;
+ goto exit;
+ }
+
+ /* Check that all required pins in the used groups are free
+ * before allocating. */
+ group_set = interfaces[ioif].groups;
+ while (NULL != (grp = get_group(group_set))) {
+ unsigned int if_group_use = 0;
+
+ switch (grp->group) {
+ case group_a:
+ if_group_use = interfaces[ioif].group_a;
+ break;
+ case group_b:
+ if_group_use = interfaces[ioif].group_b;
+ break;
+ case group_c:
+ if_group_use = interfaces[ioif].group_c;
+ break;
+ case group_d:
+ if_group_use = interfaces[ioif].group_d;
+ break;
+ case group_e:
+ if_group_use = interfaces[ioif].group_e;
+ break;
+ case group_f:
+ if_group_use = interfaces[ioif].group_f;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if (if_group_use & grp->used) {
+ printk(KERN_INFO "cris_request_io_interface: group "
+ "%s needed by %s not available\n",
+ grp->name, interfaces[ioif].name);
+ res = -EBUSY;
+ goto exit;
+ }
+
+ group_set = clear_group_from_set(group_set, grp);
+ }
+
+ /* Are the required GPIO pins available too? */
+ if (((interfaces[ioif].gpio_g_in & gpio_in_pins) !=
+ interfaces[ioif].gpio_g_in) ||
+ ((interfaces[ioif].gpio_g_out & gpio_out_pins) !=
+ interfaces[ioif].gpio_g_out) ||
+ ((interfaces[ioif].gpio_b & gpio_pb_pins) !=
+ interfaces[ioif].gpio_b)) {
+ printk(KERN_CRIT "cris_request_io_interface: Could not get "
+ "required pins for interface %u\n", ioif);
+ res = -EBUSY;
+ goto exit;
+ }
+
+ /* Check which registers need to be reconfigured. */
+ gens = genconfig_shadow;
+ gens_ii = gen_config_ii_shadow;
+
+ set_gen_config = 1;
+ switch (ioif)
+ {
+ /* Begin Non-multiplexed interfaces */
+ case if_eth:
+ /* fall through */
+ case if_serial_0:
+ set_gen_config = 0;
+ break;
+ /* End Non-multiplexed interfaces */
+ case if_serial_1:
+ set_gen_config_ii = 1;
+ SETS(gens_ii, R_GEN_CONFIG_II, sermode1, async);
+ break;
+ case if_serial_2:
+ SETS(gens, R_GEN_CONFIG, ser2, select);
+ break;
+ case if_serial_3:
+ SETS(gens, R_GEN_CONFIG, ser3, select);
+ set_gen_config_ii = 1;
+ SETS(gens_ii, R_GEN_CONFIG_II, sermode3, async);
+ break;
+ case if_sync_serial_1:
+ set_gen_config_ii = 1;
+ SETS(gens_ii, R_GEN_CONFIG_II, sermode1, sync);
+ break;
+ case if_sync_serial_3:
+ SETS(gens, R_GEN_CONFIG, ser3, select);
+ set_gen_config_ii = 1;
+ SETS(gens_ii, R_GEN_CONFIG_II, sermode3, sync);
+ break;
+ case if_shared_ram:
+ SETS(gens, R_GEN_CONFIG, mio, select);
+ break;
+ case if_shared_ram_w:
+ SETS(gens, R_GEN_CONFIG, mio_w, select);
+ break;
+ case if_par_0:
+ SETS(gens, R_GEN_CONFIG, par0, select);
+ break;
+ case if_par_1:
+ SETS(gens, R_GEN_CONFIG, par1, select);
+ break;
+ case if_par_w:
+ SETS(gens, R_GEN_CONFIG, par0, select);
+ SETS(gens, R_GEN_CONFIG, par_w, select);
+ break;
+ case if_scsi8_0:
+ SETS(gens, R_GEN_CONFIG, scsi0, select);
+ break;
+ case if_scsi8_1:
+ SETS(gens, R_GEN_CONFIG, scsi1, select);
+ break;
+ case if_scsi_w:
+ SETS(gens, R_GEN_CONFIG, scsi0, select);
+ SETS(gens, R_GEN_CONFIG, scsi0w, select);
+ break;
+ case if_ata:
+ SETS(gens, R_GEN_CONFIG, ata, select);
+ break;
+ case if_csp:
+ /* fall through */
+ case if_i2c:
+ set_gen_config = 0;
+ break;
+ case if_usb_1:
+ SETS(gens, R_GEN_CONFIG, usb1, select);
+ break;
+ case if_usb_2:
+ SETS(gens, R_GEN_CONFIG, usb2, select);
+ break;
+ case if_gpio_grp_a:
+ /* GPIO groups are only accounted, don't do configuration changes. */
+ /* fall through */
+ case if_gpio_grp_b:
+ /* fall through */
+ case if_gpio_grp_c:
+ /* fall through */
+ case if_gpio_grp_d:
+ /* fall through */
+ case if_gpio_grp_e:
+ /* fall through */
+ case if_gpio_grp_f:
+ set_gen_config = 0;
+ break;
+ default:
+ printk(KERN_INFO "cris_request_io_interface: Bad interface "
+ "%u submitted for %s\n",
+ ioif, device_id);
+ res = -EBUSY;
+ goto exit;
+ }
+
+ /* All needed I/O pins and pin groups are free, allocate. */
+ group_set = interfaces[ioif].groups;
+ while (NULL != (grp = get_group(group_set))) {
+ unsigned int if_group_use = 0;
+
+ switch (grp->group) {
+ case group_a:
+ if_group_use = interfaces[ioif].group_a;
+ break;
+ case group_b:
+ if_group_use = interfaces[ioif].group_b;
+ break;
+ case group_c:
+ if_group_use = interfaces[ioif].group_c;
+ break;
+ case group_d:
+ if_group_use = interfaces[ioif].group_d;
+ break;
+ case group_e:
+ if_group_use = interfaces[ioif].group_e;
+ break;
+ case group_f:
+ if_group_use = interfaces[ioif].group_f;
+ break;
+ default:
+ BUG_ON(1);
+ }
+ grp->used |= if_group_use;
+
+ group_set = clear_group_from_set(group_set, grp);
+ }
+
+ interfaces[ioif].used = 1;
+ interfaces[ioif].owner = (char*)device_id;
+
+ if (set_gen_config) {
+ volatile int i;
+ genconfig_shadow = gens;
+ *R_GEN_CONFIG = genconfig_shadow;
+ /* Wait 12 cycles before doing any DMA command */
+ for(i = 6; i > 0; i--)
+ nop();
+ }
+ if (set_gen_config_ii) {
+ gen_config_ii_shadow = gens_ii;
+ *R_GEN_CONFIG_II = gen_config_ii_shadow;
+ }
+
+ DBG(printk(KERN_DEBUG "GPIO pins: available before: "
+ "g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+ DBG(printk(KERN_DEBUG
+ "grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ interfaces[ioif].gpio_g_in,
+ interfaces[ioif].gpio_g_out,
+ interfaces[ioif].gpio_b));
+
+ gpio_in_pins &= ~interfaces[ioif].gpio_g_in;
+ gpio_out_pins &= ~interfaces[ioif].gpio_g_out;
+ gpio_pb_pins &= ~interfaces[ioif].gpio_b;
+
+ DBG(printk(KERN_DEBUG "GPIO pins: available after: "
+ "g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+
+exit:
+ local_irq_restore(flags);
+ if (res == 0)
+ notify_watchers();
+ return res;
+}
+
+
+void cris_free_io_interface(enum cris_io_interface ioif)
+{
+ struct if_group *grp;
+ unsigned char group_set;
+ unsigned long flags;
+
+ (void)cris_io_interface_init();
+
+ if ((ioif >= if_max_interfaces) || (ioif < 0)) {
+ printk(KERN_CRIT "cris_free_io_interface: Bad interface %u\n",
+ ioif);
+ return;
+ }
+ local_irq_save(flags);
+ if (!interfaces[ioif].used) {
+ printk(KERN_CRIT "cris_free_io_interface: Freeing free interface %u\n",
+ ioif);
+ local_irq_restore(flags);
+ return;
+ }
+ group_set = interfaces[ioif].groups;
+ while (NULL != (grp = get_group(group_set))) {
+ unsigned int if_group_use = 0;
+
+ switch (grp->group) {
+ case group_a:
+ if_group_use = interfaces[ioif].group_a;
+ break;
+ case group_b:
+ if_group_use = interfaces[ioif].group_b;
+ break;
+ case group_c:
+ if_group_use = interfaces[ioif].group_c;
+ break;
+ case group_d:
+ if_group_use = interfaces[ioif].group_d;
+ break;
+ case group_e:
+ if_group_use = interfaces[ioif].group_e;
+ break;
+ case group_f:
+ if_group_use = interfaces[ioif].group_f;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ if ((grp->used & if_group_use) != if_group_use)
+ BUG_ON(1);
+ grp->used = grp->used & ~if_group_use;
+
+ group_set = clear_group_from_set(group_set, grp);
+ }
+ interfaces[ioif].used = 0;
+ interfaces[ioif].owner = NULL;
+
+ DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+ DBG(printk("freeing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ interfaces[ioif].gpio_g_in,
+ interfaces[ioif].gpio_g_out,
+ interfaces[ioif].gpio_b));
+
+ gpio_in_pins |= interfaces[ioif].gpio_g_in;
+ gpio_out_pins |= interfaces[ioif].gpio_g_out;
+ gpio_pb_pins |= interfaces[ioif].gpio_b;
+
+ DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+ gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+
+ local_irq_restore(flags);
+
+ notify_watchers();
+}
+
+/* Create a bitmask from bit 0 (inclusive) to bit stop_bit
+ (non-inclusive). stop_bit == 0 returns 0x0 */
+static inline unsigned int create_mask(const unsigned stop_bit)
+{
+ /* Avoid overflow */
+ if (stop_bit >= 32) {
+ return 0xffffffff;
+ }
+ return (1<<stop_bit)-1;
+}
+
+
+/* port can be 'a', 'b' or 'g' */
+int cris_io_interface_allocate_pins(const enum cris_io_interface ioif,
+ const char port,
+ const unsigned start_bit,
+ const unsigned stop_bit)
+{
+ unsigned int i;
+ unsigned int mask = 0;
+ unsigned int tmp_mask;
+ unsigned long int flags;
+ enum cris_io_interface *owners;
+
+ (void)cris_io_interface_init();
+
+ DBG(printk("cris_io_interface_allocate_pins: if=%d port=%c start=%u stop=%u\n",
+ ioif, port, start_bit, stop_bit));
+
+ if (!((start_bit <= stop_bit) &&
+ ((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
+ ((port == 'g') && (stop_bit < 32))))) {
+ return -EINVAL;
+ }
+
+ mask = create_mask(stop_bit + 1);
+ tmp_mask = create_mask(start_bit);
+ mask &= ~tmp_mask;
+
+ DBG(printk("cris_io_interface_allocate_pins: port=%c start=%u stop=%u mask=0x%08x\n",
+ port, start_bit, stop_bit, mask));
+
+ local_irq_save(flags);
+
+ switch (port) {
+ case 'a':
+ if ((gpio_pa_pins & mask) != mask) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ owners = gpio_pa_owners;
+ gpio_pa_pins &= ~mask;
+ break;
+ case 'b':
+ if ((gpio_pb_pins & mask) != mask) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ owners = gpio_pb_owners;
+ gpio_pb_pins &= ~mask;
+ break;
+ case 'g':
+ if (((gpio_in_pins & mask) != mask) ||
+ ((gpio_out_pins & mask) != mask)) {
+ local_irq_restore(flags);
+ return -EBUSY;
+ }
+ owners = gpio_pg_owners;
+ gpio_in_pins &= ~mask;
+ gpio_out_pins &= ~mask;
+ break;
+ default:
+ local_irq_restore(flags);
+ return -EINVAL;
+ }
+
+ for (i = start_bit; i <= stop_bit; i++) {
+ owners[i] = ioif;
+ }
+ local_irq_restore(flags);
+
+ notify_watchers();
+ return 0;
+}
+
+
+/* port can be 'a', 'b' or 'g' */
+int cris_io_interface_free_pins(const enum cris_io_interface ioif,
+ const char port,
+ const unsigned start_bit,
+ const unsigned stop_bit)
+{
+ unsigned int i;
+ unsigned int mask = 0;
+ unsigned int tmp_mask;
+ unsigned long int flags;
+ enum cris_io_interface *owners;
+
+ (void)cris_io_interface_init();
+
+ if (!((start_bit <= stop_bit) &&
+ ((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
+ ((port == 'g') && (stop_bit < 32))))) {
+ return -EINVAL;
+ }
+
+ mask = create_mask(stop_bit + 1);
+ tmp_mask = create_mask(start_bit);
+ mask &= ~tmp_mask;
+
+ DBG(printk("cris_io_interface_free_pins: port=%c start=%u stop=%u mask=0x%08x\n",
+ port, start_bit, stop_bit, mask));
+
+ local_irq_save(flags);
+
+ switch (port) {
+ case 'a':
+ if ((~gpio_pa_pins & mask) != mask) {
+ local_irq_restore(flags);
+ printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+ }
+ owners = gpio_pa_owners;
+ break;
+ case 'b':
+ if ((~gpio_pb_pins & mask) != mask) {
+ local_irq_restore(flags);
+ printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+ }
+ owners = gpio_pb_owners;
+ break;
+ case 'g':
+ if (((~gpio_in_pins & mask) != mask) ||
+ ((~gpio_out_pins & mask) != mask)) {
+ local_irq_restore(flags);
+ printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+ }
+ owners = gpio_pg_owners;
+ break;
+ default:
+ owners = NULL; /* Cannot happen. Shut up, gcc! */
+ }
+
+ for (i = start_bit; i <= stop_bit; i++) {
+ if (owners[i] != ioif) {
+ printk(KERN_CRIT "cris_io_interface_free_pins: Freeing unowned pins");
+ }
+ }
+
+ /* All was ok, change data. */
+ switch (port) {
+ case 'a':
+ gpio_pa_pins |= mask;
+ break;
+ case 'b':
+ gpio_pb_pins |= mask;
+ break;
+ case 'g':
+ gpio_in_pins |= mask;
+ gpio_out_pins |= mask;
+ break;
+ }
+
+ for (i = start_bit; i <= stop_bit; i++) {
+ owners[i] = if_unclaimed;
+ }
+ local_irq_restore(flags);
+ notify_watchers();
+
+ return 0;
+}
+
+
+int cris_io_interface_register_watcher(void (*notify)(const unsigned int gpio_in_available,
+ const unsigned int gpio_out_available,
+ const unsigned char pa_available,
+ const unsigned char pb_available))
+{
+ struct watcher *w;
+
+ (void)cris_io_interface_init();
+
+ if (NULL == notify) {
+ return -EINVAL;
+ }
+ w = kmalloc(sizeof(*w), GFP_KERNEL);
+ if (!w) {
+ return -ENOMEM;
+ }
+ w->notify = notify;
+ w->next = watchers;
+ watchers = w;
+
+ w->notify((const unsigned int)gpio_in_pins,
+ (const unsigned int)gpio_out_pins,
+ (const unsigned char)gpio_pa_pins,
+ (const unsigned char)gpio_pb_pins);
+
+ return 0;
+}
+
+void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available,
+ const unsigned int gpio_out_available,
+ const unsigned char pa_available,
+ const unsigned char pb_available))
+{
+ struct watcher *w = watchers, *prev = NULL;
+
+ (void)cris_io_interface_init();
+
+ while ((NULL != w) && (w->notify != notify)){
+ prev = w;
+ w = w->next;
+ }
+ if (NULL != w) {
+ if (NULL != prev) {
+ prev->next = w->next;
+ } else {
+ watchers = w->next;
+ }
+ kfree(w);
+ return;
+ }
+ printk(KERN_WARNING "cris_io_interface_delete_watcher: Deleting unknown watcher 0x%p\n", notify);
+}
+
+
+static int cris_io_interface_init(void)
+{
+ static int first = 1;
+ int i;
+
+ if (!first) {
+ return 0;
+ }
+ first = 0;
+
+ for (i = 0; i<8; i++) {
+ gpio_pa_owners[i] = if_unclaimed;
+ gpio_pb_owners[i] = if_unclaimed;
+ gpio_pg_owners[i] = if_unclaimed;
+ }
+ for (; i<32; i++) {
+ gpio_pg_owners[i] = if_unclaimed;
+ }
+ return 0;
+}
+
+
+module_init(cris_io_interface_init);
+
+
+EXPORT_SYMBOL(cris_request_io_interface);
+EXPORT_SYMBOL(cris_free_io_interface);
+EXPORT_SYMBOL(cris_io_interface_allocate_pins);
+EXPORT_SYMBOL(cris_io_interface_free_pins);
+EXPORT_SYMBOL(cris_io_interface_register_watcher);
+EXPORT_SYMBOL(cris_io_interface_delete_watcher);
diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c
new file mode 100644
index 000000000..09cae80a8
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/irq.c
@@ -0,0 +1,235 @@
+/*
+ * 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 <asm/current.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
+#define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
+
+extern void kgdb_init(void);
+extern void breakpoint(void);
+
+/* 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 | 0x20000) /* ethernet tx interrupt needs to block rx */
+BUILD_IRQ(17, 0x20000 | 0x10000) /* ...and vice versa */
+BUILD_IRQ(18, 0x40000)
+BUILD_IRQ(19, 0x80000)
+BUILD_IRQ(20, 0x100000)
+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 enable_crisv10_irq(struct irq_data *data)
+{
+ crisv10_unmask_irq(data->irq);
+}
+
+static void disable_crisv10_irq(struct irq_data *data)
+{
+ crisv10_mask_irq(data->irq);
+}
+
+static struct irq_chip crisv10_irq_type = {
+ .name = "CRISv10",
+ .irq_shutdown = disable_crisv10_irq,
+ .irq_enable = enable_crisv10_irq,
+ .irq_disable = disable_crisv10_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 */
+
+extern void do_IRQ(int irq, struct pt_regs * regs);
+
+/* Handle multiple IRQs */
+void do_multiple_IRQ(struct pt_regs* regs)
+{
+ int bit;
+ unsigned masked;
+ unsigned mask;
+ unsigned ethmask = 0;
+
+ /* Get interrupts to mask and handle */
+ mask = masked = *R_VECT_MASK_RD;
+
+ /* Never mask timer IRQ */
+ mask &= ~(IO_MASK(R_VECT_MASK_RD, timer0));
+
+ /*
+ * If either ethernet interrupt (rx or tx) is active then block
+ * the other one too. Unblock afterwards also.
+ */
+ if (mask &
+ (IO_STATE(R_VECT_MASK_RD, dma0, active) |
+ IO_STATE(R_VECT_MASK_RD, dma1, active))) {
+ ethmask = (IO_MASK(R_VECT_MASK_RD, dma0) |
+ IO_MASK(R_VECT_MASK_RD, dma1));
+ }
+
+ /* Block them */
+ *R_VECT_MASK_CLR = (mask | ethmask);
+
+ /* An extra irq_enter here to prevent softIRQs to run after
+ * each do_IRQ. This will decrease the interrupt latency.
+ */
+ irq_enter();
+
+ /* Handle all IRQs */
+ for (bit = 2; bit < 32; bit++) {
+ if (masked & (1 << bit)) {
+ do_IRQ(bit, regs);
+ }
+ }
+
+ /* This irq_exit() will trigger the soft IRQs. */
+ irq_exit();
+
+ /* Unblock the IRQs again */
+ *R_VECT_MASK_SET = (masked | ethmask);
+}
+
+/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
+ setting the irq vector table.
+*/
+
+void __init init_IRQ(void)
+{
+ int i;
+
+ /* clear all interrupt masks */
+ *R_IRQ_MASK0_CLR = 0xffffffff;
+ *R_IRQ_MASK1_CLR = 0xffffffff;
+ *R_IRQ_MASK2_CLR = 0xffffffff;
+ *R_VECT_MASK_CLR = 0xffffffff;
+
+ for (i = 0; i < 256; i++)
+ etrax_irv->v[i] = weird_irq;
+
+ /* Initialize IRQ handler descriptors. */
+ for(i = 2; i < NR_IRQS; i++) {
+ irq_set_chip_and_handler(i, &crisv10_irq_type,
+ handle_simple_irq);
+ set_int_vector(i, interrupt[i]);
+ }
+
+ /* 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);
+
+ /* 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 000000000..22d846bfc
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/kgdb.c
@@ -0,0 +1,1144 @@
+/*!**************************************************************************
+*!
+*! 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
+*!
+*!---------------------------------------------------------------------------
+*!
+*! (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 <linux/reboot.h>
+
+#include <asm/setup.h>
+#include <asm/ptrace.h>
+
+#include <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;
+
+/* 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);
+
+/******************** 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
+
+/********************************** 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
+
+/* 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. */
+registers cris_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 cris_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'.
+*/
+
+/********************************** Breakpoint *******************************/
+/* Use an internal stack in the breakpoint and interrupt response routines */
+#define INTERNAL_STACK_SIZE 1024
+char internal_stack[INTERNAL_STACK_SIZE];
+
+/* Due to the breakpoint return pointer, a state variable is needed to keep
+ track of whether it is a static (compiled) or dynamic (gdb-invoked)
+ breakpoint to be handled. A static breakpoint uses the content of register
+ 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(hex_asc, *s1, base)) != NULL; ++s1)
+ x = x * base + (sd - hex_asc);
+
+ if (endptr)
+ {
+ /* Unconverted suffix is stored in endptr unless endptr is NULL. */
+ *endptr = s1;
+ }
+
+ return x;
+}
+
+/********************************** Packet I/O ******************************/
+/* Returns the integer equivalent of a hexadecimal character. */
+static int
+hex (char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f'))
+ return (ch - 'a' + 10);
+ if ((ch >= '0') && (ch <= '9'))
+ return (ch - '0');
+ if ((ch >= 'A') && (ch <= 'F'))
+ return (ch - 'A' + 10);
+ return (-1);
+}
+
+/* Convert the memory, pointed to by mem into hexadecimal representation.
+ Put the result in buf, and return a pointer to the last character
+ in buf (null). */
+
+static char *
+mem2hex(char *buf, unsigned char *mem, int count)
+{
+ int i;
+ int ch;
+
+ if (mem == NULL) {
+ /* 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 = hex_byte_pack(buf, ch);
+ }
+ }
+
+ /* Terminate properly. */
+ *buf = '\0';
+ return (buf);
+}
+
+/* Convert the array, in hexadecimal representation, pointed to by buf into
+ binary representation. Put the result in mem, and return a pointer to
+ the character after the last byte written. */
+static unsigned char*
+hex2mem (unsigned char *mem, char *buf, int count)
+{
+ int i;
+ unsigned char ch;
+ for (i = 0; i < count; i++) {
+ ch = hex (*buf++) << 4;
+ ch = ch + hex (*buf++);
+ *mem++ = ch;
+ }
+ return (mem);
+}
+
+/* Put the content of the array, in binary representation, pointed to by buf
+ into memory pointed to by mem, and return a pointer to the character after
+ the last byte written.
+ Gdb will escape $, #, and the escape char (0x7d). */
+static unsigned char*
+bin2mem (unsigned char *mem, unsigned char *buf, int count)
+{
+ int i;
+ unsigned char *next;
+ for (i = 0; i < count; i++) {
+ /* Check for any escaped characters. Be paranoid and
+ only unescape chars that should be escaped. */
+ if (*buf == 0x7d) {
+ next = buf + 1;
+ if (*next == 0x3 || *next == 0x4 || *next == 0x5D) /* #, $, ESC */
+ {
+ buf++;
+ *buf += 0x20;
+ }
+ }
+ *mem++ = *buf++;
+ }
+ return (mem);
+}
+
+/* Await the sequence $<data>#<checksum> and store <data> in the array buffer
+ returned. */
+static void
+getpacket (char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ char ch;
+ do {
+ while ((ch = getDebugChar ()) != '$')
+ /* Wait for the start character $ and ignore all other characters */;
+ checksum = 0;
+ xmitcsum = -1;
+ count = 0;
+ /* Read until a # or the end of the buffer is reached */
+ while (count < BUFMAX - 1) {
+ 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(hex_asc_hi(checksum));
+ putDebugChar(hex_asc_lo(checksum));
+ } while(kgdb_started && (getDebugChar() != '+'));
+}
+
+/* The string str is prepended with the GDB printout token and sent. Required
+ in traditional implementations. */
+void
+putDebugString (const unsigned char *str, int length)
+{
+ remcomOutBuffer[0] = 'O';
+ mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length);
+ putpacket(remcomOutBuffer);
+}
+
+/********************************* Register image ****************************/
+/* Write a value to a specified register in the register image of the current
+ thread. Returns status code SUCCESS, E02 or E05. */
+static int
+write_register (int regno, char *val)
+{
+ int status = SUCCESS;
+ registers *current_reg = &cris_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;
+}
+
+/* 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 = &cris_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;
+ }
+}
+
+/********************************** 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 = hex_byte_pack(ptr, sigval);
+
+ /* Send register contents. We probably only need to send the
+ * PC, frame pointer and stack pointer here. Other registers will be
+ * explicitly 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 = hex_byte_pack(ptr, regno);
+ *ptr++ = ':';
+
+ ptr = mem2hex(ptr, (unsigned char *)&reg_cont,
+ register_size[regno]);
+ *ptr++ = ';';
+ }
+
+ }
+
+ /* null-terminate and send it off */
+
+ *ptr = 0;
+
+ putpacket (remcomOutBuffer);
+}
+
+/* Performs a complete re-start from scratch. */
+static void
+kill_restart (void)
+{
+ machine_restart("");
+}
+
+/* All expected commands are sent from remote.c. Send a response according
+ to the description in remote.c. */
+void
+handle_exception (int sigval)
+{
+ /* 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. */
+
+ mem2hex(remcomOutBuffer, (char *)&cris_reg, sizeof(registers));
+ break;
+
+ case 'G':
+ /* Write registers. GXX..XX
+ Each byte of register data is described by two hex digits.
+ Success: OK
+ Failure: void. */
+ hex2mem((char *)&cris_reg, &remcomInBuffer[1], sizeof(registers));
+ 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;
+ 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') {
+ cris_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] = hex_asc_hi(sigval);
+ remcomOutBuffer[2] = hex_asc_lo(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;
+
+ 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);
+ }
+}
+
+/********************************** 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 ("\n"
+" .global kgdb_handle_breakpoint\n"
+"kgdb_handle_breakpoint:\n"
+";;\n"
+";; Response to the break-instruction\n"
+";;\n"
+";; Create a register image of the caller\n"
+";;\n"
+" move $dccr,[cris_reg+0x5E] ; Save the flags in DCCR before disable interrupts\n"
+" di ; Disable interrupts\n"
+" move.d $r0,[cris_reg] ; Save R0\n"
+" move.d $r1,[cris_reg+0x04] ; Save R1\n"
+" move.d $r2,[cris_reg+0x08] ; Save R2\n"
+" move.d $r3,[cris_reg+0x0C] ; Save R3\n"
+" move.d $r4,[cris_reg+0x10] ; Save R4\n"
+" move.d $r5,[cris_reg+0x14] ; Save R5\n"
+" move.d $r6,[cris_reg+0x18] ; Save R6\n"
+" move.d $r7,[cris_reg+0x1C] ; Save R7\n"
+" move.d $r8,[cris_reg+0x20] ; Save R8\n"
+" move.d $r9,[cris_reg+0x24] ; Save R9\n"
+" move.d $r10,[cris_reg+0x28] ; Save R10\n"
+" move.d $r11,[cris_reg+0x2C] ; Save R11\n"
+" move.d $r12,[cris_reg+0x30] ; Save R12\n"
+" move.d $r13,[cris_reg+0x34] ; Save R13\n"
+" move.d $sp,[cris_reg+0x38] ; Save SP (R14)\n"
+";; Due to the old assembler-versions BRP might not be recognized\n"
+" .word 0xE670 ; move brp,$r0\n"
+" subq 2,$r0 ; Set to address of previous instruction.\n"
+" move.d $r0,[cris_reg+0x3c] ; Save the address in PC (R15)\n"
+" clear.b [cris_reg+0x40] ; Clear P0\n"
+" move $vr,[cris_reg+0x41] ; Save special register P1\n"
+" clear.w [cris_reg+0x42] ; Clear P4\n"
+" move $ccr,[cris_reg+0x44] ; Save special register CCR\n"
+" move $mof,[cris_reg+0x46] ; P7\n"
+" clear.d [cris_reg+0x4A] ; Clear P8\n"
+" move $ibr,[cris_reg+0x4E] ; P9,\n"
+" move $irp,[cris_reg+0x52] ; P10,\n"
+" move $srp,[cris_reg+0x56] ; P11,\n"
+" move $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n"
+" ; P13, register DCCR already saved\n"
+";; Due to the old assembler-versions BRP might not be recognized\n"
+" .word 0xE670 ; move brp,r0\n"
+";; Static (compiled) breakpoints must return to the next instruction in order\n"
+";; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction\n"
+";; in order to execute it when execution is continued.\n"
+" test.b [is_dyn_brkp] ; Is this a dynamic breakpoint?\n"
+" beq is_static ; No, a static breakpoint\n"
+" nop\n"
+" subq 2,$r0 ; rerun the instruction the break replaced\n"
+"is_static:\n"
+" moveq 1,$r1\n"
+" move.b $r1,[is_dyn_brkp] ; Set the state variable to dynamic breakpoint\n"
+" move.d $r0,[cris_reg+0x62] ; Save the return address in BRP\n"
+" move $usp,[cris_reg+0x66] ; USP\n"
+";;\n"
+";; Handle the communication\n"
+";;\n"
+" move.d internal_stack+1020,$sp ; Use the internal stack which grows upward\n"
+" moveq 5,$r10 ; SIGTRAP\n"
+" jsr handle_exception ; Interactive routine\n"
+";;\n"
+";; Return to the caller\n"
+";;\n"
+" move.d [cris_reg],$r0 ; Restore R0\n"
+" move.d [cris_reg+0x04],$r1 ; Restore R1\n"
+" move.d [cris_reg+0x08],$r2 ; Restore R2\n"
+" move.d [cris_reg+0x0C],$r3 ; Restore R3\n"
+" move.d [cris_reg+0x10],$r4 ; Restore R4\n"
+" move.d [cris_reg+0x14],$r5 ; Restore R5\n"
+" move.d [cris_reg+0x18],$r6 ; Restore R6\n"
+" move.d [cris_reg+0x1C],$r7 ; Restore R7\n"
+" move.d [cris_reg+0x20],$r8 ; Restore R8\n"
+" move.d [cris_reg+0x24],$r9 ; Restore R9\n"
+" move.d [cris_reg+0x28],$r10 ; Restore R10\n"
+" move.d [cris_reg+0x2C],$r11 ; Restore R11\n"
+" move.d [cris_reg+0x30],$r12 ; Restore R12\n"
+" move.d [cris_reg+0x34],$r13 ; Restore R13\n"
+";;\n"
+";; FIXME: Which registers should be restored?\n"
+";;\n"
+" move.d [cris_reg+0x38],$sp ; Restore SP (R14)\n"
+" move [cris_reg+0x56],$srp ; Restore the subroutine return pointer.\n"
+" move [cris_reg+0x5E],$dccr ; Restore DCCR\n"
+" move [cris_reg+0x66],$usp ; Restore USP\n"
+" jump [cris_reg+0x62] ; A jump to the content in register BRP works.\n"
+" nop ;\n"
+"\n");
+
+/* 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 ("\n"
+" .global kgdb_handle_serial\n"
+"kgdb_handle_serial:\n"
+";;\n"
+";; Response to a serial interrupt\n"
+";;\n"
+"\n"
+" move $dccr,[cris_reg+0x5E] ; Save the flags in DCCR\n"
+" di ; Disable interrupts\n"
+" move.d $r0,[cris_reg] ; Save R0\n"
+" move.d $r1,[cris_reg+0x04] ; Save R1\n"
+" move.d $r2,[cris_reg+0x08] ; Save R2\n"
+" move.d $r3,[cris_reg+0x0C] ; Save R3\n"
+" move.d $r4,[cris_reg+0x10] ; Save R4\n"
+" move.d $r5,[cris_reg+0x14] ; Save R5\n"
+" move.d $r6,[cris_reg+0x18] ; Save R6\n"
+" move.d $r7,[cris_reg+0x1C] ; Save R7\n"
+" move.d $r8,[cris_reg+0x20] ; Save R8\n"
+" move.d $r9,[cris_reg+0x24] ; Save R9\n"
+" move.d $r10,[cris_reg+0x28] ; Save R10\n"
+" move.d $r11,[cris_reg+0x2C] ; Save R11\n"
+" move.d $r12,[cris_reg+0x30] ; Save R12\n"
+" move.d $r13,[cris_reg+0x34] ; Save R13\n"
+" move.d $sp,[cris_reg+0x38] ; Save SP (R14)\n"
+" move $irp,[cris_reg+0x3c] ; Save the address in PC (R15)\n"
+" clear.b [cris_reg+0x40] ; Clear P0\n"
+" move $vr,[cris_reg+0x41] ; Save special register P1,\n"
+" clear.w [cris_reg+0x42] ; Clear P4\n"
+" move $ccr,[cris_reg+0x44] ; Save special register CCR\n"
+" move $mof,[cris_reg+0x46] ; P7\n"
+" clear.d [cris_reg+0x4A] ; Clear P8\n"
+" move $ibr,[cris_reg+0x4E] ; P9,\n"
+" move $irp,[cris_reg+0x52] ; P10,\n"
+" move $srp,[cris_reg+0x56] ; P11,\n"
+" move $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n"
+" ; P13, register DCCR already saved\n"
+";; Due to the old assembler-versions BRP might not be recognized\n"
+" .word 0xE670 ; move brp,r0\n"
+" move.d $r0,[cris_reg+0x62] ; Save the return address in BRP\n"
+" move $usp,[cris_reg+0x66] ; USP\n"
+"\n"
+";; get the serial character (from debugport.c) and check if it is a ctrl-c\n"
+"\n"
+" jsr getDebugChar\n"
+" cmp.b 3, $r10\n"
+" bne goback\n"
+" nop\n"
+"\n"
+" move.d [cris_reg+0x5E], $r10 ; Get DCCR\n"
+" btstq 8, $r10 ; Test the U-flag.\n"
+" bmi goback\n"
+" nop\n"
+"\n"
+";;\n"
+";; Handle the communication\n"
+";;\n"
+" move.d internal_stack+1020,$sp ; Use the internal stack\n"
+" moveq 2,$r10 ; SIGINT\n"
+" jsr handle_exception ; Interactive routine\n"
+"\n"
+"goback:\n"
+";;\n"
+";; Return to the caller\n"
+";;\n"
+" move.d [cris_reg],$r0 ; Restore R0\n"
+" move.d [cris_reg+0x04],$r1 ; Restore R1\n"
+" move.d [cris_reg+0x08],$r2 ; Restore R2\n"
+" move.d [cris_reg+0x0C],$r3 ; Restore R3\n"
+" move.d [cris_reg+0x10],$r4 ; Restore R4\n"
+" move.d [cris_reg+0x14],$r5 ; Restore R5\n"
+" move.d [cris_reg+0x18],$r6 ; Restore R6\n"
+" move.d [cris_reg+0x1C],$r7 ; Restore R7\n"
+" move.d [cris_reg+0x20],$r8 ; Restore R8\n"
+" move.d [cris_reg+0x24],$r9 ; Restore R9\n"
+" move.d [cris_reg+0x28],$r10 ; Restore R10\n"
+" move.d [cris_reg+0x2C],$r11 ; Restore R11\n"
+" move.d [cris_reg+0x30],$r12 ; Restore R12\n"
+" move.d [cris_reg+0x34],$r13 ; Restore R13\n"
+";;\n"
+";; FIXME: Which registers should be restored?\n"
+";;\n"
+" move.d [cris_reg+0x38],$sp ; Restore SP (R14)\n"
+" move [cris_reg+0x56],$srp ; Restore the subroutine return pointer.\n"
+" move [cris_reg+0x5E],$dccr ; Restore DCCR\n"
+" move [cris_reg+0x66],$usp ; Restore USP\n"
+" reti ; Return from the interrupt routine\n"
+" nop\n"
+"\n");
+
+/* 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 000000000..02b783457
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/process.c
@@ -0,0 +1,193 @@
+/*
+ * 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/sched.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <arch/svinto.h>
+#include <linux/init.h>
+#include <arch/system.h>
+#include <linux/ptrace.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
+ local_irq_enable();
+}
+
+/*
+ * 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)
+ extern int cause_of_death;
+#endif
+
+ printk("*** HARD RESET ***\n");
+ local_irq_disable();
+
+#if defined(CONFIG_ETRAX_WATCHDOG)
+ cause_of_death = 0xbedead;
+#else
+ /* Since we dont plan to keep on resetting 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 task_pt_regs(t)->irp;
+}
+
+/* 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);
+asmlinkage void ret_from_kernel_thread(void);
+
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+ unsigned long arg, struct task_struct *p)
+{
+ struct pt_regs *childregs = task_pt_regs(p);
+ struct switch_stack *swstack = ((struct switch_stack *)childregs) - 1;
+
+ /* put the pt_regs structure at the end of the new kernel stack page and fix it up
+ * remember that the task_struct doubles as the kernel stack for the task
+ */
+
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(swstack, 0,
+ sizeof(struct switch_stack) + sizeof(struct pt_regs));
+ swstack->r1 = usp;
+ swstack->r2 = arg;
+ childregs->dccr = 1 << I_DCCR_BITNR;
+ swstack->return_ip = (unsigned long) ret_from_kernel_thread;
+ p->thread.ksp = (unsigned long) swstack;
+ p->thread.usp = 0;
+ return 0;
+ }
+ *childregs = *current_pt_regs(); /* struct copy of pt_regs */
+
+ childregs->r10 = 0; /* child returns 0 after a fork/clone */
+
+ /* put the switch stack right below the pt_regs */
+
+ 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 ?: rdusp();
+
+ /* 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;
+}
+
+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();
+
+ show_regs_print_info(KERN_DEFAULT);
+
+ 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 000000000..bfddfb994
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/ptrace.c
@@ -0,0 +1,202 @@
+/*
+ * 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/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/signal.h>
+#include <linux/security.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+
+/*
+ * 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 *)task_pt_regs(task))[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 *)task_pt_regs(task))[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? */
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+}
+
+/*
+ * 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).
+ */
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ int ret;
+ unsigned int regno = addr >> 2;
+ unsigned long __user *datap = (unsigned long __user *)data;
+
+ switch (request) {
+ /* Read word at location address. */
+ case PTRACE_PEEKTEXT:
+ case PTRACE_PEEKDATA:
+ ret = generic_ptrace_peekdata(child, addr, data);
+ break;
+
+ /* Read the word at location address in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || regno > PT_MAX)
+ break;
+
+ tmp = get_reg(child, regno);
+ ret = put_user(tmp, datap);
+ break;
+ }
+
+ /* Write the word at location address. */
+ case PTRACE_POKETEXT:
+ case PTRACE_POKEDATA:
+ ret = generic_ptrace_pokedata(child, addr, data);
+ break;
+
+ /* Write the word at location address in the USER area. */
+ case PTRACE_POKEUSR:
+ ret = -EIO;
+ if ((addr & 3) || regno > PT_MAX)
+ break;
+
+ if (regno == PT_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, regno, data))
+ break;
+ ret = 0;
+ break;
+
+ /* Get all GP registers from the child. */
+ case PTRACE_GETREGS: {
+ int i;
+ unsigned long tmp;
+
+ ret = 0;
+ for (i = 0; i <= PT_MAX; i++) {
+ tmp = get_reg(child, i);
+
+ if (put_user(tmp, datap)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ datap++;
+ }
+
+ break;
+ }
+
+ /* Set all GP registers in the child. */
+ case PTRACE_SETREGS: {
+ int i;
+ unsigned long tmp;
+
+ ret = 0;
+ for (i = 0; i <= PT_MAX; i++) {
+ if (get_user(tmp, datap)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ if (i == PT_DCCR) {
+ tmp &= DCCR_MASK;
+ tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
+ }
+
+ put_reg(child, i, tmp);
+ datap++;
+ }
+
+ break;
+ }
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
+ 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 000000000..7ab31f1c7
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/setup.c
@@ -0,0 +1,106 @@
+/*
+ *
+ * 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/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/param.h>
+#include <arch/system.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 >= ARRAY_SIZE(cpu_info))
+ info = &cpu_info[ARRAY_SIZE(cpu_info) - 1];
+ else
+ info = &cpu_info[revision];
+
+ 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);
+
+ return 0;
+}
+
+#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 000000000..2454a0b02
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/shadows.c
@@ -0,0 +1,36 @@
+/*
+ * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
+ */
+
+/* Shadows for internal Etrax-registers */
+
+unsigned long genconfig_shadow;
+unsigned long gen_config_ii_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 LEDs 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 000000000..7122d9773
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/signal.c
@@ -0,0 +1,438 @@
+/*
+ * 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-2007 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (bjornw@axis.com)
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.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>
+#include <arch/system.h>
+
+#define DEBUG_SIG 0
+
+/* 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;
+
+void do_signal(int canrestart, struct pt_regs *regs);
+
+/*
+ * 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->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;
+}
+
+asmlinkage int sys_sigreturn(void)
+{
+ struct pt_regs *regs = current_pt_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;
+
+ set_current_blocked(&set);
+
+ 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;
+}
+
+asmlinkage int sys_rt_sigreturn(void)
+{
+ struct pt_regs *regs = current_pt_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;
+
+ set_current_blocked(&set);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+
+ if (restore_altstack(&frame->uc.uc_stack))
+ 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 ksignal *ksig, size_t frame_size)
+{
+ unsigned long sp = sigsp(rdusp(), ksig);
+
+ /* make sure the frame is dword-aligned */
+
+ sp &= ~3;
+
+ return (void __user*)(sp - frame_size);
+}
+
+/* grab and setup a signal frame.
+ *
+ * basically 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 int setup_frame(struct ksignal *ksig, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct sigframe __user *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ksig, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+ if (err)
+ return -EFAULT;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask));
+ }
+ if (err)
+ return -EFAULT;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ksig->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)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+
+ regs->irp = (unsigned long) ksig->ka.sa.sa_handler; /* what we enter NOW */
+ regs->srp = return_ip; /* what we enter LATER */
+ regs->r10 = ksig->sig; /* first argument is signo */
+
+ /* actually move the usp to reflect the stacked frame */
+
+ wrusp((unsigned long)frame);
+
+ return 0;
+}
+
+static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ unsigned long return_ip;
+ int err = 0;
+
+ frame = get_sigframe(ksig, sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, &ksig->info);
+ if (err)
+ return -EFAULT;
+
+ /* Clear all the bits of the ucontext we don't use. */
+ err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
+
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ err |= __save_altstack(&frame->uc.uc_stack, rdusp());
+
+ if (err)
+ return -EFAULT;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ksig->ka.sa.sa_flags & SA_RESTORER) {
+ return_ip = (unsigned long)ksig->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)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+
+ /* What we enter NOW */
+ regs->irp = (unsigned long) ksig->ka.sa.sa_handler;
+ /* What we enter LATER */
+ regs->srp = return_ip;
+ /* First argument is signo */
+ regs->r10 = ksig->sig;
+ /* Second argument is (siginfo_t *) */
+ regs->r11 = (unsigned long)&frame->info;
+ /* Third argument is unused */
+ regs->r12 = 0;
+
+ /* Actually move the usp to reflect the stacked frame */
+ wrusp((unsigned long)frame);
+
+ return 0;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+static inline void handle_signal(int canrestart, struct ksignal *ksig,
+ struct pt_regs *regs)
+{
+ sigset_t *oldset = sigmask_to_save();
+ int ret;
+
+ /* 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 (!(ksig->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 (ksig->ka.sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame(ksig, oldset, regs);
+ else
+ ret = setup_frame(ksig, oldset, regs);
+
+ signal_setup_done(ret, ksig, 0);
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Also note that the regs structure given here as an argument, is the latest
+ * pushed pt_regs. It may or may not be the same as the first pushed registers
+ * when the initial usermode->kernelmode transition took place. Therefore
+ * we can use user_mode(regs) to see if we came directly from kernel or user
+ * mode below.
+ */
+
+void do_signal(int canrestart, struct pt_regs *regs)
+{
+ struct ksignal ksig;
+
+ /*
+ * 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;
+
+ if (get_signal(&ksig)) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(canrestart, &ksig, regs);
+ return;
+ }
+
+ /* 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->r9 = __NR_restart_syscall;
+ regs->irp -= 2;
+ }
+ }
+
+ /* if there's no signal to deliver, we just put the saved sigmask
+ * back */
+ restore_saved_sigmask();
+}
diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c
new file mode 100644
index 000000000..b5eb5cd2f
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/time.c
@@ -0,0 +1,267 @@
+/*
+ * linux/arch/cris/arch-v10/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ * Copyright (C) 1999-2002 Axis Communications AB
+ *
+ */
+
+#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 <linux/mm.h>
+#include <asm/types.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/irq_regs.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
+
+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);
+ 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;
+}
+
+static u32 cris_v10_gettimeoffset(void)
+{
+ u32 count;
+
+ /* 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.
+ */
+ count = *R_TIMER0_DATA;
+
+ /* Convert timer value to nsec */
+ return (TIMER0_DIV - count) * (NSEC_PER_SEC/HZ)/TIMER0_DIV;
+}
+
+/* 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
+
+#ifdef CONFIG_ETRAX_WATCHDOG
+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)
+ /* 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)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG
+ 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
+}
+
+
+extern void cris_do_profile(struct pt_regs *regs);
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "xtime_update()" routine every clocktick
+ */
+static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+ struct pt_regs *regs = get_irq_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();
+
+ /* Update statistics. */
+ update_process_times(user_mode(regs));
+
+ /* call the real timer interrupt handler */
+ xtime_update(1);
+
+ cris_do_profile(regs); /* Save profiling information */
+ return IRQ_HANDLED;
+}
+
+/* timer is IRQF_SHARED so drivers can add stuff to the timer irq chain */
+
+static struct irqaction irq2 = {
+ .handler = timer_interrupt,
+ .flags = IRQF_SHARED,
+ .name = "timer",
+};
+
+void __init time_init(void)
+{
+ arch_gettimeoffset = cris_v10_gettimeoffset;
+
+ /* 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;
+
+ /* 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
+
+ /* unmask the timer irq */
+ *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer0, set);
+
+ /* now actually register the 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)
+ 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 000000000..7001beda7
--- /dev/null
+++ b/arch/cris/arch-v10/kernel/traps.c
@@ -0,0 +1,131 @@
+/*
+ * Helper functions for trap handlers
+ *
+ * Copyright (C) 2000-2007, Axis Communications AB.
+ *
+ * Authors: Bjorn Wesen
+ * Hans-Peter Nilsson
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <asm/uaccess.h>
+#include <arch/sv_addr_ag.h>
+#include <arch/system.h>
+
+void
+show_registers(struct pt_regs *regs)
+{
+ /*
+ * It's possible to use either the USP register or current->thread.usp.
+ * USP might not correspond to the current process for all cases this
+ * function is called, and current->thread.usp isn't up to date for the
+ * current process. Experience shows that using USP is the way to go.
+ */
+ unsigned long usp = rdusp();
+
+ 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 sp: %08lx\n",
+ regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs);
+
+ printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
+
+ printk("Process %s (pid: %d, stackpage=%08lx)\n",
+ current->comm, current->pid, (unsigned long)current);
+
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (!user_mode(regs)) {
+ int i;
+
+ show_stack(NULL, (unsigned long *)usp);
+
+ /*
+ * If the previous stack-dump wasn't a kernel one, dump the
+ * kernel stack now.
+ */
+ if (usp != 0)
+ show_stack(NULL, NULL);
+
+ printk("\nCode: ");
+
+ if (regs->irp < PAGE_OFFSET)
+ goto bad_value;
+
+ /*
+ * Quite often the value at regs->irp doesn't point to the
+ * interesting instruction, which often is the previous
+ * instruction. So dump at an offset large enough that the
+ * instruction decoding should be in sync at the interesting
+ * point, but small enough to fit on a row. The regs->irp
+ * location is pointed out in a ksymoops-friendly way by
+ * wrapping the byte for that address in parenthesises.
+ */
+ for (i = -12; i < 12; i++) {
+ unsigned char c;
+
+ if (__get_user(c, &((unsigned char *)regs->irp)[i])) {
+bad_value:
+ printk(" Bad IP value.");
+ break;
+ }
+
+ if (i == 0)
+ printk("(%02x) ", c);
+ else
+ printk("%02x ", c);
+ }
+ printk("\n");
+ }
+}
+
+void
+arch_enable_nmi(void)
+{
+ asm volatile ("setf m");
+}
+
+extern void (*nmi_handler)(struct pt_regs *);
+void handle_nmi(struct pt_regs *regs)
+{
+ if (nmi_handler)
+ nmi_handler(regs);
+
+ /* Wait until nmi is no longer active. (We enable NMI immediately after
+ returning from this function, and we don't want it happening while
+ exiting from the NMI interrupt handler.) */
+ while (*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active))
+ ;
+}
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+void
+handle_BUG(struct pt_regs *regs)
+{
+ struct bug_frame f;
+ unsigned char c;
+ unsigned long irp = regs->irp;
+
+ if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f))
+ return;
+ if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC)
+ return;
+ if (__get_user(c, f.filename))
+ f.filename = "<bad filename>";
+
+ printk("kernel BUG at %s:%d!\n", f.filename, f.line);
+}
+#endif