mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
17
arch/cris/arch-v10/kernel/Makefile
Normal file
17
arch/cris/arch-v10/kernel/Makefile
Normal file
|
@ -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:
|
||||
|
16
arch/cris/arch-v10/kernel/crisksyms.c
Normal file
16
arch/cris/arch-v10/kernel/crisksyms.c
Normal file
|
@ -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);
|
559
arch/cris/arch-v10/kernel/debugport.c
Normal file
559
arch/cris/arch-v10/kernel/debugport.c
Normal file
|
@ -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);
|
287
arch/cris/arch-v10/kernel/dma.c
Normal file
287
arch/cris/arch-v10/kernel/dma.c
Normal file
|
@ -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);
|
969
arch/cris/arch-v10/kernel/entry.S
Normal file
969
arch/cris/arch-v10/kernel/entry.S
Normal file
|
@ -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
|
||||
|
841
arch/cris/arch-v10/kernel/fasttimer.c
Normal file
841
arch/cris/arch-v10/kernel/fasttimer.c
Normal file
|
@ -0,0 +1,841 @@
|
|||
/*
|
||||
* 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) {
|
||||
if (seq_printf(m, debug_log_string[i], debug_log_value[i]) < 0)
|
||||
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];
|
||||
if (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) < 0)
|
||||
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];
|
||||
if (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) < 0)
|
||||
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];
|
||||
if (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) < 0)
|
||||
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);
|
||||
if (seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
|
||||
"d: %6li us data: 0x%08lX"
|
||||
/* " func: 0x%08lX" */
|
||||
"\n",
|
||||
t->name,
|
||||
(unsigned long)t->tv_set.tv_jiff,
|
||||
(unsigned long)t->tv_set.tv_usec,
|
||||
(unsigned long)t->tv_expires.tv_jiff,
|
||||
(unsigned long)t->tv_expires.tv_usec,
|
||||
t->delay_us,
|
||||
t->data
|
||||
/* , t->function */
|
||||
) < 0)
|
||||
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);
|
723
arch/cris/arch-v10/kernel/head.S
Normal file
723
arch/cris/arch-v10/kernel/head.S
Normal file
|
@ -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"
|
1182
arch/cris/arch-v10/kernel/io_interface_mux.c
Normal file
1182
arch/cris/arch-v10/kernel/io_interface_mux.c
Normal file
File diff suppressed because it is too large
Load diff
235
arch/cris/arch-v10/kernel/irq.c
Normal file
235
arch/cris/arch-v10/kernel/irq.c
Normal file
|
@ -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
|
||||
}
|
1144
arch/cris/arch-v10/kernel/kgdb.c
Normal file
1144
arch/cris/arch-v10/kernel/kgdb.c
Normal file
File diff suppressed because it is too large
Load diff
193
arch/cris/arch-v10/kernel/process.c
Normal file
193
arch/cris/arch-v10/kernel/process.c
Normal file
|
@ -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);
|
||||
}
|
||||
|
202
arch/cris/arch-v10/kernel/ptrace.c
Normal file
202
arch/cris/arch-v10/kernel/ptrace.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
104
arch/cris/arch-v10/kernel/setup.c
Normal file
104
arch/cris/arch-v10/kernel/setup.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
*
|
||||
* 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];
|
||||
|
||||
return seq_printf(m,
|
||||
"processor\t: 0\n"
|
||||
"cpu\t\t: CRIS\n"
|
||||
"cpu revision\t: %lu\n"
|
||||
"cpu model\t: %s\n"
|
||||
"cache size\t: %d kB\n"
|
||||
"fpu\t\t: %s\n"
|
||||
"mmu\t\t: %s\n"
|
||||
"mmu DMA bug\t: %s\n"
|
||||
"ethernet\t: %s Mbps\n"
|
||||
"token ring\t: %s\n"
|
||||
"scsi\t\t: %s\n"
|
||||
"ata\t\t: %s\n"
|
||||
"usb\t\t: %s\n"
|
||||
"bogomips\t: %lu.%02lu\n",
|
||||
|
||||
revision,
|
||||
info->model,
|
||||
info->cache,
|
||||
info->flags & HAS_FPU ? "yes" : "no",
|
||||
info->flags & HAS_MMU ? "yes" : "no",
|
||||
info->flags & HAS_MMU_BUG ? "yes" : "no",
|
||||
info->flags & HAS_ETHERNET100 ? "10/100" : "10",
|
||||
info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
|
||||
info->flags & HAS_SCSI ? "yes" : "no",
|
||||
info->flags & HAS_ATA ? "yes" : "no",
|
||||
info->flags & HAS_USB ? "yes" : "no",
|
||||
(loops_per_jiffy * HZ + 500) / 500000,
|
||||
((loops_per_jiffy * HZ + 500) / 5000) % 100);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
void
|
||||
show_etrax_copyright(void)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"Linux/CRIS port on ETRAX 100LX (c) 2001 Axis Communications AB\n");
|
||||
}
|
36
arch/cris/arch-v10/kernel/shadows.c
Normal file
36
arch/cris/arch-v10/kernel/shadows.c
Normal file
|
@ -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;
|
||||
|
440
arch/cris/arch-v10/kernel/signal.c
Normal file
440
arch/cris/arch-v10/kernel/signal.c
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* 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_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/* restore the regs from &sc->regs (same as sc, since regs is first)
|
||||
* (sc is already checked for VERIFY_READ since the sigframe was
|
||||
* checked in sys_sigreturn previously)
|
||||
*/
|
||||
|
||||
if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
|
||||
goto badframe;
|
||||
|
||||
/* make sure the U-flag is set so user-mode cannot fool us */
|
||||
|
||||
regs->dccr |= 1 << 8;
|
||||
|
||||
/* restore the old USP as it was before we stacked the sc etc.
|
||||
* (we cannot just pop the sigcontext since we aligned the sp and
|
||||
* stuff after pushing it)
|
||||
*/
|
||||
|
||||
err |= __get_user(old_usp, &sc->usp);
|
||||
|
||||
wrusp(old_usp);
|
||||
|
||||
/* TODO: the other ports use regs->orig_XX to disable syscall checks
|
||||
* after this completes, but we don't use that mechanism. maybe we can
|
||||
* use it now ?
|
||||
*/
|
||||
|
||||
return err;
|
||||
|
||||
badframe:
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* TODO what is the current->exec_domain stuff and invmap ? */
|
||||
|
||||
/* 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();
|
||||
}
|
267
arch/cris/arch-v10/kernel/time.c
Normal file
267
arch/cris/arch-v10/kernel/time.c
Normal file
|
@ -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
|
||||
}
|
131
arch/cris/arch-v10/kernel/traps.c
Normal file
131
arch/cris/arch-v10/kernel/traps.c
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue