Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,18 @@
#
# Makefile for the linux kernel.
#
extra-y := head.o
obj-y := entry.o traps.o irq.o debugport.o \
process.o ptrace.o setup.o signal.o traps.o time.o \
cache.o cacheflush.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o
obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
obj-$(CONFIG_MODULES) += crisksyms.o
clean:

View file

@ -0,0 +1,33 @@
#include <linux/module.h>
#include <asm/io.h>
#include <arch/cache.h>
#include <arch/hwregs/dma.h>
/* This file is used to workaround a cache bug, Guinness TR 106. */
inline void flush_dma_descr(struct dma_descr_data *descr, int flush_buf)
{
/* Flush descriptor to make sure we get correct in_eop and after. */
asm volatile ("ftagd [%0]" :: "r" (descr));
/* Flush buffer pointed out by descriptor. */
if (flush_buf)
cris_flush_cache_range(phys_to_virt((unsigned)descr->buf),
(unsigned)(descr->after - descr->buf));
}
EXPORT_SYMBOL(flush_dma_descr);
void flush_dma_list(struct dma_descr_data *descr)
{
while (1) {
flush_dma_descr(descr, 1);
if (descr->eol)
break;
descr = phys_to_virt((unsigned)descr->next);
}
}
EXPORT_SYMBOL(flush_dma_list);
/* From cacheflush.S */
EXPORT_SYMBOL(cris_flush_cache);
/* From cacheflush.S */
EXPORT_SYMBOL(cris_flush_cache_range);

View file

@ -0,0 +1,99 @@
.global cris_flush_cache_range
.type cris_flush_cache_range, @function
cris_flush_cache_range:
move.d 1024, $r12
cmp.d $r11, $r12
bhi cris_flush_1KB
nop
add.d $r10, $r11
ftagd [$r10]
cris_flush_last:
addq 32, $r10
cmp.d $r11, $r10
blt cris_flush_last
ftagd [$r10]
ret
nop
cris_flush_1KB:
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ba cris_flush_cache_range
sub.d $r12, $r11
.size cris_flush_cache_range, . - cris_flush_cache_range
.global cris_flush_cache
.type cris_flush_cache, @function
cris_flush_cache:
moveq 0, $r10
cris_flush_line:
move.d 16*1024, $r11
addq 16, $r10
cmp.d $r10, $r11
blt cris_flush_line
fidxd [$r10]
ret
nop
.size cris_flush_cache, . - cris_flush_cache

View file

@ -0,0 +1,28 @@
#include <linux/module.h>
#include <linux/irq.h>
#include <arch/dma.h>
#include <arch/intmem.h>
#include <mach/pinmux.h>
#include <arch/io.h>
/* Functions for allocating DMA channels */
EXPORT_SYMBOL(crisv32_request_dma);
EXPORT_SYMBOL(crisv32_free_dma);
/* Functions for handling internal RAM */
EXPORT_SYMBOL(crisv32_intmem_alloc);
EXPORT_SYMBOL(crisv32_intmem_free);
EXPORT_SYMBOL(crisv32_intmem_phys_to_virt);
EXPORT_SYMBOL(crisv32_intmem_virt_to_phys);
/* Functions for handling pinmux */
EXPORT_SYMBOL(crisv32_pinmux_alloc);
EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed);
EXPORT_SYMBOL(crisv32_pinmux_dealloc);
EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed);
EXPORT_SYMBOL(crisv32_io_get_name);
EXPORT_SYMBOL(crisv32_io_get);
/* Functions masking/unmasking interrupts */
EXPORT_SYMBOL(crisv32_mask_irq);
EXPORT_SYMBOL(crisv32_unmask_irq);

View file

@ -0,0 +1,203 @@
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/console.h>
#include <linux/init.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/ser_defs.h>
#include <hwregs/dma_defs.h>
#include <mach/pinmux.h>
struct dbg_port
{
unsigned char nbr;
unsigned long instance;
unsigned int started;
unsigned long baudrate;
unsigned char parity;
unsigned int bits;
};
struct dbg_port ports[] =
{
{
0,
regi_ser0,
0,
115200,
'N',
8
},
{
1,
regi_ser1,
0,
115200,
'N',
8
},
{
2,
regi_ser2,
0,
115200,
'N',
8
},
{
3,
regi_ser3,
0,
115200,
'N',
8
},
#if CONFIG_ETRAX_SERIAL_PORTS == 5
{
4,
regi_ser4,
0,
115200,
'N',
8
},
#endif
};
static struct dbg_port *port =
#if defined(CONFIG_ETRAX_DEBUG_PORT0)
&ports[0];
#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
&ports[1];
#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
&ports[2];
#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
&ports[3];
#elif defined(CONFIG_ETRAX_DEBUG_PORT4)
&ports[4];
#else
NULL;
#endif
#ifdef CONFIG_ETRAX_KGDB
static struct dbg_port *kgdb_port =
#if defined(CONFIG_ETRAX_KGDB_PORT0)
&ports[0];
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
&ports[1];
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
&ports[2];
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
&ports[3];
#elif defined(CONFIG_ETRAX_KGDB_PORT4)
&ports[4];
#else
NULL;
#endif
#endif
static void
start_port(struct dbg_port* p)
{
if (!p)
return;
if (p->started)
return;
p->started = 1;
if (p->nbr == 1)
crisv32_pinmux_alloc_fixed(pinmux_ser1);
else if (p->nbr == 2)
crisv32_pinmux_alloc_fixed(pinmux_ser2);
else if (p->nbr == 3)
crisv32_pinmux_alloc_fixed(pinmux_ser3);
#if CONFIG_ETRAX_SERIAL_PORTS == 5
else if (p->nbr == 4)
crisv32_pinmux_alloc_fixed(pinmux_ser4);
#endif
/* Set up serial port registers */
reg_ser_rw_tr_ctrl tr_ctrl = {0};
reg_ser_rw_tr_dma_en tr_dma_en = {0};
reg_ser_rw_rec_ctrl rec_ctrl = {0};
reg_ser_rw_tr_baud_div tr_baud_div = {0};
reg_ser_rw_rec_baud_div rec_baud_div = {0};
tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493;
tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no;
tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8;
tr_ctrl.en = rec_ctrl.en = 1;
if (p->parity == 'O')
{
tr_ctrl.par_en = regk_ser_yes;
tr_ctrl.par = regk_ser_odd;
rec_ctrl.par_en = regk_ser_yes;
rec_ctrl.par = regk_ser_odd;
}
else if (p->parity == 'E')
{
tr_ctrl.par_en = regk_ser_yes;
tr_ctrl.par = regk_ser_even;
rec_ctrl.par_en = regk_ser_yes;
rec_ctrl.par = regk_ser_odd;
}
if (p->bits == 7)
{
tr_ctrl.data_bits = regk_ser_bits7;
rec_ctrl.data_bits = regk_ser_bits7;
}
REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div);
REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div);
REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en);
REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl);
REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl);
}
#ifdef CONFIG_ETRAX_KGDB
/* Use polling to get a single character from the kernel debug port */
int
getDebugChar(void)
{
reg_ser_rs_stat_din stat;
reg_ser_rw_ack_intr ack_intr = { 0 };
do {
stat = REG_RD(ser, kgdb_port->instance, rs_stat_din);
} while (!stat.dav);
/* Ack the data_avail interrupt. */
ack_intr.dav = 1;
REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr);
return stat.data;
}
/* Use polling to put a single character to the kernel debug port */
void
putDebugChar(int val)
{
reg_ser_r_stat_din stat;
do {
stat = REG_RD(ser, kgdb_port->instance, r_stat_din);
} while (!stat.tr_rdy);
REG_WR_INT(ser, kgdb_port->instance, rw_dout, val);
}
#endif /* CONFIG_ETRAX_KGDB */
/* Register console for printk's, etc. */
int __init
init_etrax_debug(void)
{
start_port(port);
#ifdef CONFIG_ETRAX_KGDB
start_port(kgdb_port);
#endif /* CONFIG_ETRAX_KGDB */
return 0;
}

View file

@ -0,0 +1,915 @@
/*
* Copyright (C) 2000-2003 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
*
* Code for the system-call and fault low-level handling routines.
*
* NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call.
*
* Stack layout in 'ret_from_system_call':
* ptrace needs to have all regs on the stack.
* if the order here is changed, it needs to be
* updated in fork.c:copy_process, signal.c:do_signal,
* ptrace.c and ptrace.h
*
*/
#include <linux/linkage.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <asm/errno.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <hwregs/asm/reg_map_asm.h>
#include <hwregs/asm/intr_vect_defs_asm.h>
;; Exported functions.
.globl system_call
.globl ret_from_intr
.globl ret_from_fork
.globl ret_from_kernel_thread
.globl resume
.globl multiple_interrupt
.globl nmi_interrupt
.globl spurious_interrupt
.globl do_sigtrap
.globl gdb_handle_exception
.globl sys_call_table
; Check if preemptive kernel scheduling should be done.
#ifdef CONFIG_PREEMPT
_resume_kernel:
di
; Load current task struct.
movs.w -8192, $r0 ; THREAD_SIZE = 8192
and.d $sp, $r0
addoq +TI_preempt_count, $r0, $acr
move.d [$acr], $r10 ; Preemption disabled?
bne _Rexit
nop
_need_resched:
addoq +TI_flags, $r0, $acr
move.d [$acr], $r10
btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set.
bpl _Rexit
nop
; Do preemptive kernel scheduling.
jsr preempt_schedule_irq
nop
; Load new task struct.
movs.w -8192, $r0 ; THREAD_SIZE = 8192.
and.d $sp, $r0
; One more time with new task.
ba _need_resched
nop
#else
#define _resume_kernel _Rexit
#endif
; Called at exit from fork. schedule_tail must be called to drop
; spinlock if CONFIG_PREEMPT.
.type ret_from_fork,@function
ret_from_fork:
jsr schedule_tail
nop
ba ret_from_sys_call
nop
.size ret_from_fork, . - ret_from_fork
.type ret_from_kernel_thread,@function
ret_from_kernel_thread:
jsr schedule_tail
nop
move.d $r2, $r10
jsr $r1
nop
moveq 0, $r9 ; no syscall restarts, TYVM...
ba ret_from_sys_call
nop
.size ret_from_kernel_thread, . - ret_from_kernel_thread
.type ret_from_intr,@function
ret_from_intr:
;; Check for resched if preemptive kernel, or if we're going back to
;; user-mode. This test matches the user_regs(regs) macro. Don't simply
;; test CCS since that doesn't necessarily reflect what mode we'll
;; return into.
addoq +PT_ccs, $sp, $acr
move.d [$acr], $r0
btstq 16, $r0 ; User-mode flag.
bpl _resume_kernel
.size ret_from_intr, . - ret_from_intr + 2 ; +2 includes the dslot.
; Note that di below is in delay slot.
.type _resume_userspace,@function
_resume_userspace:
di ; So need_resched and sigpending don't change.
movs.w -8192, $r0 ; THREAD_SIZE == 8192
and.d $sp, $r0
addoq +TI_flags, $r0, $acr ; current->work
move.d [$acr], $r10
and.d _TIF_WORK_MASK, $r10 ; Work to be done on return?
bne _work_pending
nop
ba _Rexit
nop
.size _resume_userspace, . - _resume_userspace
;; The system_call is called by a BREAK instruction, which looks pretty
;; much like any other exception.
;;
;; System calls can't be made from interrupts but we still stack ERP
;; to have a complete stack frame.
;;
;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12,
;; r13,mof,srp
;;
;; This function looks on the _surface_ like spaghetti programming, but it's
;; really designed so that the fast-path does not force cache-loading of
;; non-used instructions. Only the non-common cases cause the outlined code
;; to run..
.type system_call,@function
system_call:
;; Stack-frame similar to the irq heads, which is reversed in
;; ret_from_sys_call.
sub.d 92, $sp ; Skip EXS and EDA.
movem $r13, [$sp]
move.d $sp, $r8
addq 14*4, $r8
move.d $acr, $r0
move $srs, $r1
move $mof, $r2
move $spc, $r3
move $ccs, $r4
move $srp, $r5
move $erp, $r6
subq 4, $sp
movem $r6, [$r8]
ei ; Enable interrupts while processing syscalls.
move.d $r10, [$sp]
; Set S-bit when kernel debugging to keep hardware breakpoints active.
#ifdef CONFIG_ETRAX_KGDB
move $ccs, $r0
or.d (1<<9), $r0
move $r0, $ccs
#endif
movs.w -ENOSYS, $r0
addoq +PT_r10, $sp, $acr
move.d $r0, [$acr]
;; Check if this process is syscall-traced.
movs.w -8192, $r0 ; THREAD_SIZE == 8192
and.d $sp, $r0
addoq +TI_flags, $r0, $acr
move.d [$acr], $r0
btstq TIF_SYSCALL_TRACE, $r0
bmi _syscall_trace_entry
nop
_syscall_traced:
;; Check for sanity in the requested syscall number.
cmpu.w NR_syscalls, $r9
bhs ret_from_sys_call
lslq 2, $r9 ; Multiply by 4, in the delay slot.
;; The location on the stack for the register structure is passed as a
;; seventh argument. Some system calls need this.
move.d $sp, $r0
subq 4, $sp
move.d $r0, [$sp]
;; The registers carrying parameters (R10-R13) are intact. The optional
;; fifth and sixth parameters is in MOF and SRP respectively. Put them
;; back on the stack.
subq 4, $sp
move $srp, [$sp]
subq 4, $sp
move $mof, [$sp]
;; Actually to the system call.
addo.d +sys_call_table, $r9, $acr
move.d [$acr], $acr
jsr $acr
nop
addq 3*4, $sp ; Pop the mof, srp and regs parameters.
addoq +PT_r10, $sp, $acr
move.d $r10, [$acr] ; Save the return value.
moveq 1, $r9 ; "Parameter" to ret_from_sys_call to
; show it was a sys call.
;; Fall through into ret_from_sys_call to return.
ret_from_sys_call:
;; R9 is a parameter:
;; >= 1 from syscall
;; 0 from irq
;; Get the current task-struct pointer.
movs.w -8192, $r0 ; THREAD_SIZE == 8192
and.d $sp, $r0
di ; Make sure need_resched and sigpending don't change.
addoq +TI_flags, $r0, $acr
move.d [$acr], $r1
and.d _TIF_ALLWORK_MASK, $r1
bne _syscall_exit_work
nop
.size system_call, . - system_call
.type _Rexit,@function
_Rexit:
;; This epilogue MUST match the prologues in multiple_interrupt, irq.h
;; and ptregs.h.
addq 4, $sp ; Skip orig_r10.
movem [$sp+], $r13 ; Registers R0-R13.
move.d [$sp+], $acr
move [$sp], $srs
addq 4, $sp
move [$sp+], $mof
move [$sp+], $spc
move [$sp+], $ccs
move [$sp+], $srp
move [$sp+], $erp
addq 8, $sp ; Skip EXS, EDA.
jump $erp
rfe ; Restore condition code stack in delay-slot.
.size _Rexit, . - _Rexit
;; We get here after doing a syscall if extra work might need to be done
;; perform syscall exit tracing if needed.
.type _syscall_exit_work,@function
_syscall_exit_work:
;; R0 contains current at this point and irq's are disabled.
addoq +TI_flags, $r0, $acr
move.d [$acr], $r1
btstq TIF_SYSCALL_TRACE, $r1
bpl _work_pending
nop
ei
move.d $r9, $r1 ; Preserve R9.
jsr do_syscall_trace
nop
move.d $r1, $r9
ba _resume_userspace
nop
.size _syscall_exit_work, . - _syscall_exit_work
.type _work_pending,@function
_work_pending:
addoq +TI_flags, $r0, $acr
move.d [$acr], $r10
btstq TIF_NEED_RESCHED, $r10 ; Need resched?
bpl _work_notifysig ; No, must be signal/notify.
nop
.size _work_pending, . - _work_pending
.type _work_resched,@function
_work_resched:
move.d $r9, $r1 ; Preserve R9.
jsr schedule
nop
move.d $r1, $r9
di
addoq +TI_flags, $r0, $acr
move.d [$acr], $r1
and.d _TIF_WORK_MASK, $r1 ; Ignore sycall trace counter.
beq _Rexit
nop
btstq TIF_NEED_RESCHED, $r1
bmi _work_resched ; current->work.need_resched.
nop
.size _work_resched, . - _work_resched
.type _work_notifysig,@function
_work_notifysig:
;; Deal with pending signals and notify-resume requests.
addoq +TI_flags, $r0, $acr
move.d [$acr], $r12 ; The thread_info_flags parameter.
move.d $sp, $r11 ; The regs param.
jsr do_notify_resume
move.d $r9, $r10 ; do_notify_resume syscall/irq param.
ba _Rexit
nop
.size _work_notifysig, . - _work_notifysig
;; We get here as a sidetrack when we've entered a syscall with the
;; trace-bit set. We need to call do_syscall_trace and then continue
;; with the call.
_syscall_trace_entry:
;; PT_r10 in the frame contains -ENOSYS as required, at this point.
jsr do_syscall_trace
nop
;; Now re-enter the syscall code to do the syscall itself. We need to
;; restore R9 here to contain the wanted syscall, and the other
;; parameter-bearing registers.
addoq +PT_r9, $sp, $acr
move.d [$acr], $r9
addoq +PT_orig_r10, $sp, $acr
move.d [$acr], $r10 ; PT_r10 is already -ENOSYS.
addoq +PT_r11, $sp, $acr
move.d [$acr], $r11
addoq +PT_r12, $sp, $acr
move.d [$acr], $r12
addoq +PT_r13, $sp, $acr
move.d [$acr], $r13
addoq +PT_mof, $sp, $acr
move [$acr], $mof
addoq +PT_srp, $sp, $acr
move [$acr], $srp
ba _syscall_traced
nop
;; Resume performs the actual task-switching, by switching stack
;; pointers. Input arguments are:
;;
;; R10 = prev
;; R11 = next
;; R12 = thread offset in task struct.
;;
;; Returns old current in R10.
.type resume,@function
resume:
subq 4, $sp ; Make space for srp.
add.d $r12, $r10 ; R10 = current tasks tss.
addoq +THREAD_ccs, $r10, $acr
move $srp, [$sp] ; Keep old/new PC on the stack.
move $ccs, [$acr] ; Save IRQ enable state.
di
addoq +THREAD_usp, $r10, $acr
subq 10*4, $sp ; Make room for R9.
move $usp, [$acr] ; Save user-mode stackpointer.
;; See copy_thread for the reason why register R9 is saved.
movem $r9, [$sp] ; Save non-scratch registers and R9.
addoq +THREAD_ksp, $r10, $acr
move.d $sp, $r10 ; Return last running task in R10.
move.d $sp, [$acr] ; Save kernel SP for old task.
and.d -8192, $r10 ; Get thread_info from stackpointer.
addoq +TI_task, $r10, $acr
add.d $r12, $r11 ; Find the new tasks tss.
move.d [$acr], $r10 ; Get task.
addoq +THREAD_ksp, $r11, $acr
move.d [$acr], $sp ; Switch to new stackframe.
addoq +THREAD_usp, $r11, $acr
movem [$sp+], $r9 ; Restore non-scratch registers and R9.
move [$acr], $usp ; Restore user-mode stackpointer.
addoq +THREAD_ccs, $r11, $acr
move.d [$sp+], $r11
jump $r11 ; Restore PC.
move [$acr], $ccs ; Restore IRQ enable status.
.size resume, . - resume
nmi_interrupt:
;; If we receive a watchdog interrupt while it is not expected, then set
;; up a canonical frame and dump register contents before dying.
;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
subq 12, $sp ; Skip EXS, EDA.
move $nrp, [$sp]
subq 4, $sp
move $srp, [$sp]
subq 4, $sp
move $ccs, [$sp]
subq 4, $sp
move $spc, [$sp]
subq 4, $sp
move $mof, [$sp]
subq 4, $sp
move $srs, [$sp]
subq 4, $sp
move.d $acr, [$sp]
subq 14*4, $sp ; Make room for R0-R13.
movem $r13, [$sp] ; Push R0-R13.
subq 4, $sp
move.d $r10, [$sp] ; Push orig_r10.
move.d REG_ADDR(intr_vect, regi_irq, r_nmi), $r0
move.d [$r0], $r0
btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0
bpl 1f
nop
jsr handle_watchdog_bite ; In time.c.
move.d $sp, $r10 ; Pointer to registers
1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0
bpl 1f
nop
jsr handle_nmi
move.d $sp, $r10 ; Pointer to registers
1: addq 4, $sp ; Skip orig_r10
movem [$sp+], $r13
move.d [$sp+], $acr
move [$sp], $srs
addq 4, $sp
move [$sp+], $mof
move [$sp+], $spc
move [$sp+], $ccs
move [$sp+], $srp
move [$sp+], $nrp
addq 8, $sp ; Skip EXS, EDA.
jump $nrp
rfn
.comm cause_of_death, 4 ;; Don't declare this anywhere.
spurious_interrupt:
di
jump hard_reset_now
nop
;; This handles the case when multiple interrupts arrive at the same
;; time. Jump to the first set interrupt bit in a priority fashion. The
;; hardware will call the unserved interrupts after the handler
;; finishes.
.type multiple_interrupt, @function
multiple_interrupt:
;; This prologue MUST match the one in irq.h and the struct in ptregs.h!
subq 12, $sp ; Skip EXS, EDA.
move $erp, [$sp]
subq 4, $sp
move $srp, [$sp]
subq 4, $sp
move $ccs, [$sp]
subq 4, $sp
move $spc, [$sp]
subq 4, $sp
move $mof, [$sp]
subq 4, $sp
move $srs, [$sp]
subq 4, $sp
move.d $acr, [$sp]
subq 14*4, $sp ; Make room for R0-R13.
movem $r13, [$sp] ; Push R0-R13.
subq 4, $sp
move.d $r10, [$sp] ; Push orig_r10.
; Set S-bit when kernel debugging to keep hardware breakpoints active.
#ifdef CONFIG_ETRAX_KGDB
move $ccs, $r0
or.d (1<<9), $r0
move $r0, $ccs
#endif
jsr crisv32_do_multiple
move.d $sp, $r10
jump ret_from_intr
nop
.size multiple_interrupt, . - multiple_interrupt
do_sigtrap:
;; Sigtraps the process that executed the BREAK instruction. Creates a
;; frame that Rexit expects.
subq 4, $sp
move $eda, [$sp]
subq 4, $sp
move $exs, [$sp]
subq 4, $sp
move $erp, [$sp]
subq 4, $sp
move $srp, [$sp]
subq 4, $sp
move $ccs, [$sp]
subq 4, $sp
move $spc, [$sp]
subq 4, $sp
move $mof, [$sp]
subq 4, $sp
move $srs, [$sp]
subq 4, $sp
move.d $acr, [$sp]
di ; Need to disable irq's at this point.
subq 14*4, $sp ; Make room for r0-r13.
movem $r13, [$sp] ; Push the r0-r13 registers.
subq 4, $sp
move.d $r10, [$sp] ; Push orig_r10.
movs.w -8192, $r9 ; THREAD_SIZE == 8192
and.d $sp, $r9
;; thread_info as first parameter
move.d $r9, $r10
moveq 5, $r11 ; SIGTRAP as second argument.
jsr ugdb_trap_user
nop
jump ret_from_intr ; Use the return routine for interrupts.
nop
gdb_handle_exception:
subq 4, $sp
move.d $r0, [$sp]
#ifdef CONFIG_ETRAX_KGDB
move $ccs, $r0 ; U-flag not affected by previous insns.
btstq 16, $r0 ; Test the U-flag.
bmi _ugdb_handle_exception ; Go to user mode debugging.
nop ; Empty delay-slot (cannot pop R0 here).
ba kgdb_handle_exception ; Go to kernel debugging.
move.d [$sp+], $r0 ; Restore R0 in delay slot.
#endif
_ugdb_handle_exception:
ba do_sigtrap ; SIGTRAP the offending process.
move.d [$sp+], $r0 ; Restore R0 in delay slot.
.data
.section .rodata,"a"
sys_call_table:
.long sys_restart_syscall ; 0 - old "setup()" system call, used
; for restarting.
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink /* 10 */
.long sys_execve
.long sys_chdir
.long sys_time
.long sys_mknod
.long sys_chmod /* 15 */
.long sys_lchown16
.long sys_ni_syscall /* old break syscall holder */
.long sys_stat
.long sys_lseek
.long sys_getpid /* 20 */
.long sys_mount
.long sys_oldumount
.long sys_setuid16
.long sys_getuid16
.long sys_stime /* 25 */
.long sys_ptrace
.long sys_alarm
.long sys_fstat
.long sys_pause
.long sys_utime /* 30 */
.long sys_ni_syscall /* old stty syscall holder */
.long sys_ni_syscall /* old gtty syscall holder */
.long sys_access
.long sys_nice
.long sys_ni_syscall /* 35 old ftime syscall holder */
.long sys_sync
.long sys_kill
.long sys_rename
.long sys_mkdir
.long sys_rmdir /* 40 */
.long sys_dup
.long sys_pipe
.long sys_times
.long sys_ni_syscall /* old prof syscall holder */
.long sys_brk /* 45 */
.long sys_setgid16
.long sys_getgid16
.long sys_signal
.long sys_geteuid16
.long sys_getegid16 /* 50 */
.long sys_acct
.long sys_umount /* recycled never used phys( */
.long sys_ni_syscall /* old lock syscall holder */
.long sys_ioctl
.long sys_fcntl /* 55 */
.long sys_ni_syscall /* old mpx syscall holder */
.long sys_setpgid
.long sys_ni_syscall /* old ulimit syscall holder */
.long sys_ni_syscall /* old sys_olduname holder */
.long sys_umask /* 60 */
.long sys_chroot
.long sys_ustat
.long sys_dup2
.long sys_getppid
.long sys_getpgrp /* 65 */
.long sys_setsid
.long sys_sigaction
.long sys_sgetmask
.long sys_ssetmask
.long sys_setreuid16 /* 70 */
.long sys_setregid16
.long sys_sigsuspend
.long sys_sigpending
.long sys_sethostname
.long sys_setrlimit /* 75 */
.long sys_old_getrlimit
.long sys_getrusage
.long sys_gettimeofday
.long sys_settimeofday
.long sys_getgroups16 /* 80 */
.long sys_setgroups16
.long sys_select /* was old_select in Linux/E100 */
.long sys_symlink
.long sys_lstat
.long sys_readlink /* 85 */
.long sys_uselib
.long sys_swapon
.long sys_reboot
.long sys_old_readdir
.long sys_old_mmap /* 90 */
.long sys_munmap
.long sys_truncate
.long sys_ftruncate
.long sys_fchmod
.long sys_fchown16 /* 95 */
.long sys_getpriority
.long sys_setpriority
.long sys_ni_syscall /* old profil syscall holder */
.long sys_statfs
.long sys_fstatfs /* 100 */
.long sys_ni_syscall /* sys_ioperm in i386 */
.long sys_socketcall
.long sys_syslog
.long sys_setitimer
.long sys_getitimer /* 105 */
.long sys_newstat
.long sys_newlstat
.long sys_newfstat
.long sys_ni_syscall /* old sys_uname holder */
.long sys_ni_syscall /* sys_iopl in i386 */
.long sys_vhangup
.long sys_ni_syscall /* old "idle" system call */
.long sys_ni_syscall /* vm86old in i386 */
.long sys_wait4
.long sys_swapoff /* 115 */
.long sys_sysinfo
.long sys_ipc
.long sys_fsync
.long sys_sigreturn
.long sys_clone /* 120 */
.long sys_setdomainname
.long sys_newuname
.long sys_ni_syscall /* sys_modify_ldt */
.long sys_adjtimex
.long sys_mprotect /* 125 */
.long sys_sigprocmask
.long sys_ni_syscall /* old "create_module" */
.long sys_init_module
.long sys_delete_module
.long sys_ni_syscall /* 130: old "get_kernel_syms" */
.long sys_quotactl
.long sys_getpgid
.long sys_fchdir
.long sys_bdflush
.long sys_sysfs /* 135 */
.long sys_personality
.long sys_ni_syscall /* for afs_syscall */
.long sys_setfsuid16
.long sys_setfsgid16
.long sys_llseek /* 140 */
.long sys_getdents
.long sys_select
.long sys_flock
.long sys_msync
.long sys_readv /* 145 */
.long sys_writev
.long sys_getsid
.long sys_fdatasync
.long sys_sysctl
.long sys_mlock /* 150 */
.long sys_munlock
.long sys_mlockall
.long sys_munlockall
.long sys_sched_setparam
.long sys_sched_getparam /* 155 */
.long sys_sched_setscheduler
.long sys_sched_getscheduler
.long sys_sched_yield
.long sys_sched_get_priority_max
.long sys_sched_get_priority_min /* 160 */
.long sys_sched_rr_get_interval
.long sys_nanosleep
.long sys_mremap
.long sys_setresuid16
.long sys_getresuid16 /* 165 */
.long sys_ni_syscall /* sys_vm86 */
.long sys_ni_syscall /* Old sys_query_module */
.long sys_poll
.long sys_ni_syscall /* Old nfsservctl */
.long sys_setresgid16 /* 170 */
.long sys_getresgid16
.long sys_prctl
.long sys_rt_sigreturn
.long sys_rt_sigaction
.long sys_rt_sigprocmask /* 175 */
.long sys_rt_sigpending
.long sys_rt_sigtimedwait
.long sys_rt_sigqueueinfo
.long sys_rt_sigsuspend
.long sys_pread64 /* 180 */
.long sys_pwrite64
.long sys_chown16
.long sys_getcwd
.long sys_capget
.long sys_capset /* 185 */
.long sys_sigaltstack
.long sys_sendfile
.long sys_ni_syscall /* streams1 */
.long sys_ni_syscall /* streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
.long sys_mmap2
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
.long sys_lstat64
.long sys_fstat64
.long sys_lchown
.long sys_getuid
.long sys_getgid /* 200 */
.long sys_geteuid
.long sys_getegid
.long sys_setreuid
.long sys_setregid
.long sys_getgroups /* 205 */
.long sys_setgroups
.long sys_fchown
.long sys_setresuid
.long sys_getresuid
.long sys_setresgid /* 210 */
.long sys_getresgid
.long sys_chown
.long sys_setuid
.long sys_setgid
.long sys_setfsuid /* 215 */
.long sys_setfsgid
.long sys_pivot_root
.long sys_mincore
.long sys_madvise
.long sys_getdents64 /* 220 */
.long sys_fcntl64
.long sys_ni_syscall /* reserved for TUX */
.long sys_ni_syscall
.long sys_gettid
.long sys_readahead /* 225 */
.long sys_setxattr
.long sys_lsetxattr
.long sys_fsetxattr
.long sys_getxattr
.long sys_lgetxattr /* 230 */
.long sys_fgetxattr
.long sys_listxattr
.long sys_llistxattr
.long sys_flistxattr
.long sys_removexattr /* 235 */
.long sys_lremovexattr
.long sys_fremovexattr
.long sys_tkill
.long sys_sendfile64
.long sys_futex /* 240 */
.long sys_sched_setaffinity
.long sys_sched_getaffinity
.long sys_ni_syscall /* sys_set_thread_area */
.long sys_ni_syscall /* sys_get_thread_area */
.long sys_io_setup /* 245 */
.long sys_io_destroy
.long sys_io_getevents
.long sys_io_submit
.long sys_io_cancel
.long sys_fadvise64 /* 250 */
.long sys_ni_syscall
.long sys_exit_group
.long sys_lookup_dcookie
.long sys_epoll_create
.long sys_epoll_ctl /* 255 */
.long sys_epoll_wait
.long sys_remap_file_pages
.long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime /* 260 */
.long sys_timer_gettime
.long sys_timer_getoverrun
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime /* 265 */
.long sys_clock_getres
.long sys_clock_nanosleep
.long sys_statfs64
.long sys_fstatfs64
.long sys_tgkill /* 270 */
.long sys_utimes
.long sys_fadvise64_64
.long sys_ni_syscall /* sys_vserver */
.long sys_ni_syscall /* sys_mbind */
.long sys_ni_syscall /* 275 sys_get_mempolicy */
.long sys_ni_syscall /* sys_set_mempolicy */
.long sys_mq_open
.long sys_mq_unlink
.long sys_mq_timedsend
.long sys_mq_timedreceive /* 280 */
.long sys_mq_notify
.long sys_mq_getsetattr
.long sys_ni_syscall /* reserved for kexec */
.long sys_waitid
.long sys_ni_syscall /* 285 */ /* available */
.long sys_add_key
.long sys_request_key
.long sys_keyctl
.long sys_ioprio_set
.long sys_ioprio_get /* 290 */
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
.long sys_migrate_pages
.long sys_openat /* 295 */
.long sys_mkdirat
.long sys_mknodat
.long sys_fchownat
.long sys_futimesat
.long sys_fstatat64 /* 300 */
.long sys_unlinkat
.long sys_renameat
.long sys_linkat
.long sys_symlinkat
.long sys_readlinkat /* 305 */
.long sys_fchmodat
.long sys_faccessat
.long sys_pselect6
.long sys_ppoll
.long sys_unshare /* 310 */
.long sys_set_robust_list
.long sys_get_robust_list
.long sys_splice
.long sys_sync_file_range
.long sys_tee /* 315 */
.long sys_vmsplice
.long sys_move_pages
.long sys_getcpu
.long sys_epoll_pwait
.long sys_utimensat /* 320 */
.long sys_signalfd
.long sys_timerfd_create
.long sys_eventfd
.long sys_fallocate
.long sys_timerfd_settime /* 325 */
.long sys_timerfd_gettime
.long sys_signalfd4
.long sys_eventfd2
.long sys_epoll_create1
.long sys_dup3 /* 330 */
.long sys_pipe2
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev
.long sys_setns /* 335 */
.long sys_name_to_handle_at
.long sys_open_by_handle_at
.long sys_rt_tgsigqueueinfo
.long sys_perf_event_open
.long sys_recvmmsg /* 340 */
.long sys_accept4
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
.long sys_clock_adjtime /* 345 */
.long sys_syncfs
.long sys_sendmmsg
.long sys_process_vm_readv
.long sys_process_vm_writev
.long sys_kcmp /* 350 */
.long sys_finit_module
/*
* NOTE!! This doesn't have to be exact - we just have
* to make sure we have _enough_ of the "sys_ni_syscall"
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
.rept NR_syscalls - (.-sys_call_table) / 4
.long sys_ni_syscall
.endr

View file

@ -0,0 +1,800 @@
/*
* linux/arch/cris/kernel/fasttimer.c
*
* Fast timers for ETRAX FS
*
* Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/timer_defs.h>
#include <asm/fasttimer.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
/*
* timer0 is running at 100MHz and generating jiffies timer ticks
* at 100 or 1000 HZ.
* fasttimer gives an API that gives timers that expire "between" the jiffies
* giving microsecond resolution (10 ns).
* fasttimer uses reg_timer_rw_trig register to get interrupt when
* r_time reaches a certain value.
*/
#define DEBUG_LOG_INCLUDED
#define FAST_TIMER_LOG
/* #define FAST_TIMER_TEST */
#define FAST_TIMER_SANITY_CHECKS
#ifdef FAST_TIMER_SANITY_CHECKS
static int sanity_failed;
#endif
#define D1(x)
#define D2(x)
#define DP(x)
static unsigned int fast_timer_running;
static unsigned int fast_timers_added;
static unsigned int fast_timers_started;
static unsigned int fast_timers_expired;
static unsigned int fast_timers_deleted;
static unsigned int fast_timer_is_init;
static unsigned int fast_timer_ints;
struct fast_timer *fast_timer_list = NULL;
#ifdef DEBUG_LOG_INCLUDED
#define DEBUG_LOG_MAX 128
static const char * debug_log_string[DEBUG_LOG_MAX];
static unsigned long debug_log_value[DEBUG_LOG_MAX];
static unsigned int debug_log_cnt;
static unsigned int debug_log_cnt_wrapped;
#define DEBUG_LOG(string, value) \
{ \
unsigned long log_flags; \
local_irq_save(log_flags); \
debug_log_string[debug_log_cnt] = (string); \
debug_log_value[debug_log_cnt] = (unsigned long)(value); \
if (++debug_log_cnt >= DEBUG_LOG_MAX) \
{ \
debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
debug_log_cnt_wrapped = 1; \
} \
local_irq_restore(log_flags); \
}
#else
#define DEBUG_LOG(string, value)
#endif
#define NUM_TIMER_STATS 16
#ifdef FAST_TIMER_LOG
struct fast_timer timer_added_log[NUM_TIMER_STATS];
struct fast_timer timer_started_log[NUM_TIMER_STATS];
struct fast_timer timer_expired_log[NUM_TIMER_STATS];
#endif
int timer_div_settings[NUM_TIMER_STATS];
int timer_delay_settings[NUM_TIMER_STATS];
struct work_struct fast_work;
static void
timer_trig_handler(struct work_struct *work);
/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
inline void do_gettimeofday_fast(struct fasttime_t *tv)
{
tv->tv_jiff = jiffies;
tv->tv_usec = GET_JIFFIES_USEC();
}
inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
{
/* Compare jiffies. Takes care of wrapping */
if (time_before(t0->tv_jiff, t1->tv_jiff))
return -1;
else if (time_after(t0->tv_jiff, t1->tv_jiff))
return 1;
/* Compare us */
if (t0->tv_usec < t1->tv_usec)
return -1;
else if (t0->tv_usec > t1->tv_usec)
return 1;
return 0;
}
/* Called with ints off */
inline void start_timer_trig(unsigned long delay_us)
{
reg_timer_rw_ack_intr ack_intr = { 0 };
reg_timer_rw_intr_mask intr_mask;
reg_timer_rw_trig trig;
reg_timer_rw_trig_cfg trig_cfg = { 0 };
reg_timer_r_time r_time0;
reg_timer_r_time r_time1;
unsigned char trig_wrap;
unsigned char time_wrap;
r_time0 = REG_RD(timer, regi_timer0, r_time);
D1(printk("start_timer_trig : %d us freq: %i div: %i\n",
delay_us, freq_index, div));
/* Clear trig irq */
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
intr_mask.trig = 0;
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
/* Set timer values and check if trigger wraps. */
/* r_time is 100MHz (10 ns resolution) */
trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0;
timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig;
timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
/* Ack interrupt */
ack_intr.trig = 1;
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
/* Start timer */
REG_WR(timer, regi_timer0, rw_trig, trig);
trig_cfg.tmr = regk_timer_time;
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
/* Check if we have already passed the trig time */
r_time1 = REG_RD(timer, regi_timer0, r_time);
time_wrap = r_time1 < r_time0;
if ((trig_wrap && !time_wrap) || (r_time1 < trig)) {
/* No, Enable trig irq */
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
intr_mask.trig = 1;
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
fast_timers_started++;
fast_timer_running = 1;
} else {
/* We have passed the time, disable trig point, ack intr */
trig_cfg.tmr = regk_timer_off;
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
/* call the int routine */
INIT_WORK(&fast_work, timer_trig_handler);
schedule_work(&fast_work);
}
}
/* In version 1.4 this function takes 27 - 50 us */
void start_one_shot_timer(struct fast_timer *t,
fast_timer_function_type *function,
unsigned long data,
unsigned long delay_us,
const char *name)
{
unsigned long flags;
struct fast_timer *tmp;
D1(printk("sft %s %d us\n", name, delay_us));
local_irq_save(flags);
do_gettimeofday_fast(&t->tv_set);
tmp = fast_timer_list;
#ifdef FAST_TIMER_SANITY_CHECKS
/* Check so this is not in the list already... */
while (tmp != NULL) {
if (tmp == t) {
printk(KERN_DEBUG
"timer name: %s data: 0x%08lX already "
"in list!\n", name, data);
sanity_failed++;
goto done;
} else
tmp = tmp->next;
}
tmp = fast_timer_list;
#endif
t->delay_us = delay_us;
t->function = function;
t->data = data;
t->name = name;
t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
if (t->tv_expires.tv_usec > 1000000) {
t->tv_expires.tv_usec -= 1000000;
t->tv_expires.tv_jiff += HZ;
}
#ifdef FAST_TIMER_LOG
timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
#endif
fast_timers_added++;
/* Check if this should timeout before anything else */
if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0) {
/* Put first in list and modify the timer value */
t->prev = NULL;
t->next = fast_timer_list;
if (fast_timer_list)
fast_timer_list->prev = t;
fast_timer_list = t;
#ifdef FAST_TIMER_LOG
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
#endif
start_timer_trig(delay_us);
} else {
/* Put in correct place in list */
while (tmp->next &&
fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0)
tmp = tmp->next;
/* Insert t after tmp */
t->prev = tmp;
t->next = tmp->next;
if (tmp->next)
{
tmp->next->prev = t;
}
tmp->next = t;
}
D2(printk("start_one_shot_timer: %d us done\n", delay_us));
done:
local_irq_restore(flags);
} /* start_one_shot_timer */
static inline int fast_timer_pending (const struct fast_timer * t)
{
return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
}
static inline int detach_fast_timer (struct fast_timer *t)
{
struct fast_timer *next, *prev;
if (!fast_timer_pending(t))
return 0;
next = t->next;
prev = t->prev;
if (next)
next->prev = prev;
if (prev)
prev->next = next;
else
fast_timer_list = next;
fast_timers_deleted++;
return 1;
}
int del_fast_timer(struct fast_timer * t)
{
unsigned long flags;
int ret;
local_irq_save(flags);
ret = detach_fast_timer(t);
t->next = t->prev = NULL;
local_irq_restore(flags);
return ret;
} /* del_fast_timer */
/* Interrupt routines or functions called in interrupt context */
/* Timer interrupt handler for trig interrupts */
static irqreturn_t
timer_trig_interrupt(int irq, void *dev_id)
{
reg_timer_r_masked_intr masked_intr;
/* Check if the timer interrupt is for us (a trig int) */
masked_intr = REG_RD(timer, regi_timer0, r_masked_intr);
if (!masked_intr.trig)
return IRQ_NONE;
timer_trig_handler(NULL);
return IRQ_HANDLED;
}
static void timer_trig_handler(struct work_struct *work)
{
reg_timer_rw_ack_intr ack_intr = { 0 };
reg_timer_rw_intr_mask intr_mask;
reg_timer_rw_trig_cfg trig_cfg = { 0 };
struct fast_timer *t;
unsigned long flags;
/* We keep interrupts disabled not only when we modify the
* fast timer list, but any time we hold a reference to a
* timer in the list, since del_fast_timer may be called
* from (another) interrupt context. Thus, the only time
* when interrupts are enabled is when calling the timer
* callback function.
*/
local_irq_save(flags);
/* Clear timer trig interrupt */
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
intr_mask.trig = 0;
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
/* First stop timer, then ack interrupt */
/* Stop timer */
trig_cfg.tmr = regk_timer_off;
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
/* Ack interrupt */
ack_intr.trig = 1;
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
fast_timer_running = 0;
fast_timer_ints++;
fast_timer_function_type *f;
unsigned long d;
t = fast_timer_list;
while (t) {
struct fasttime_t tv;
/* Has it really expired? */
do_gettimeofday_fast(&tv);
D1(printk(KERN_DEBUG
"t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
if (fasttime_cmp(&t->tv_expires, &tv) <= 0) {
/* Yes it has expired */
#ifdef FAST_TIMER_LOG
timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t;
#endif
fast_timers_expired++;
/* Remove this timer before call, since it may reuse the timer */
if (t->prev)
t->prev->next = t->next;
else
fast_timer_list = t->next;
if (t->next)
t->next->prev = t->prev;
t->prev = NULL;
t->next = NULL;
/* Save function callback data before enabling
* interrupts, since the timer may be removed and we
* don't know how it was allocated (e.g. ->function
* and ->data may become overwritten after deletion
* if the timer was stack-allocated).
*/
f = t->function;
d = t->data;
if (f != NULL) {
/* Run the callback function with interrupts
* enabled. */
local_irq_restore(flags);
f(d);
local_irq_save(flags);
} else
DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints);
} else {
/* Timer is to early, let's set it again using the normal routines */
D1(printk(".\n"));
}
t = fast_timer_list;
if (t != NULL) {
/* Start next timer.. */
long us = 0;
struct fasttime_t tv;
do_gettimeofday_fast(&tv);
/* time_after_eq takes care of wrapping */
if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
us = ((t->tv_expires.tv_jiff - tv.tv_jiff) *
1000000 / HZ + t->tv_expires.tv_usec -
tv.tv_usec);
if (us > 0) {
if (!fast_timer_running) {
#ifdef FAST_TIMER_LOG
timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t;
#endif
start_timer_trig(us);
}
break;
} else {
/* Timer already expired, let's handle it better late than never.
* The normal loop handles it
*/
D1(printk("e! %d\n", us));
}
}
}
local_irq_restore(flags);
if (!t)
D1(printk("ttrig stop!\n"));
}
static void wake_up_func(unsigned long data)
{
wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
wake_up(sleep_wait_p);
}
/* Useful API */
void schedule_usleep(unsigned long us)
{
struct fast_timer t;
wait_queue_head_t sleep_wait;
init_waitqueue_head(&sleep_wait);
D1(printk("schedule_usleep(%d)\n", us));
start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
"usleep");
/* Uninterruptible sleep on the fast timer. (The condition is
* somewhat redundant since the timer is what wakes us up.) */
wait_event(sleep_wait, !fast_timer_pending(&t));
D1(printk("done schedule_usleep(%d)\n", us));
}
#ifdef CONFIG_PROC_FS
/* This value is very much based on testing */
#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
static int proc_fasttimer_show(struct seq_file *m, void *v)
{
unsigned long flags;
int i = 0;
int num_to_show;
struct fasttime_t tv;
struct fast_timer *t, *nextt;
do_gettimeofday_fast(&tv);
seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
seq_printf(m, "Fast timer running: %s\n",
fast_timer_running ? "yes" : "no");
seq_printf(m, "Current time: %lu.%06lu\n",
(unsigned long)tv.tv_jiff,
(unsigned long)tv.tv_usec);
#ifdef FAST_TIMER_SANITY_CHECKS
seq_printf(m, "Sanity failed: %i\n", sanity_failed);
#endif
seq_putc(m, '\n');
#ifdef DEBUG_LOG_INCLUDED
{
int end_i = debug_log_cnt;
i = 0;
if (debug_log_cnt_wrapped)
i = debug_log_cnt;
while ((i != end_i || debug_log_cnt_wrapped)) {
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 delay: %i"
"\n",
timer_div_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 != NULL){
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("timer removed!\n");
t = nextt;
}
local_irq_restore(flags);
return 0;
}
static int proc_fasttimer_open(struct inode *inode, struct file *file)
{
return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
}
static const struct file_operations proc_fasttimer_fops = {
.open = proc_fasttimer_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* PROC_FS */
#ifdef FAST_TIMER_TEST
static volatile unsigned long i = 0;
static volatile int num_test_timeout = 0;
static struct fast_timer tr[10];
static int exp_num[10];
static struct fasttime_t tv_exp[100];
static void test_timeout(unsigned long data)
{
do_gettimeofday_fast(&tv_exp[data]);
exp_num[data] = num_test_timeout;
num_test_timeout++;
}
static void test_timeout1(unsigned long data)
{
do_gettimeofday_fast(&tv_exp[data]);
exp_num[data] = num_test_timeout;
if (data < 7)
{
start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1");
i++;
}
num_test_timeout++;
}
DP(
static char buf0[2000];
static char buf1[2000];
static char buf2[2000];
static char buf3[2000];
static char buf4[2000];
);
static char buf5[6000];
static int j_u[1000];
static void fast_timer_test(void)
{
int prev_num;
int j;
struct fasttime_t tv, tv0, tv1, tv2;
printk("fast_timer_test() start\n");
do_gettimeofday_fast(&tv);
for (j = 0; j < 1000; j++)
{
j_u[j] = GET_JIFFIES_USEC();
}
for (j = 0; j < 100; j++)
{
do_gettimeofday_fast(&tv_exp[j]);
}
printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
for (j = 0; j < 1000; j++)
{
printk(KERN_DEBUG "%i %i %i %i %i\n",
j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]);
j += 4;
}
for (j = 0; j < 100; j++)
{
printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n",
tv_exp[j].tv_jiff, tv_exp[j].tv_usec,
tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec,
tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec,
tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec,
tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec);
j += 4;
}
do_gettimeofday_fast(&tv0);
start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0");
DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0));
i++;
start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1");
DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0));
i++;
start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2");
DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0));
i++;
start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3");
DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0));
i++;
start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx");
DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0));
i++;
do_gettimeofday_fast(&tv1);
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
prev_num = num_test_timeout;
while (num_test_timeout < i)
{
if (num_test_timeout != prev_num)
prev_num = num_test_timeout;
}
do_gettimeofday_fast(&tv2);
printk(KERN_INFO "Timers started %is %06i\n",
tv0.tv_jiff, tv0.tv_usec);
printk(KERN_INFO "Timers started at %is %06i\n",
tv1.tv_jiff, tv1.tv_usec);
printk(KERN_INFO "Timers done %is %06i\n",
tv2.tv_jiff, tv2.tv_usec);
DP(printk("buf0:\n");
printk(buf0);
printk("buf1:\n");
printk(buf1);
printk("buf2:\n");
printk(buf2);
printk("buf3:\n");
printk(buf3);
printk("buf4:\n");
printk(buf4);
);
printk("buf5:\n");
printk(buf5);
printk("timers set:\n");
for(j = 0; j<i; j++)
{
struct fast_timer *t = &tr[j];
printk("%-10s set: %6is %06ius exp: %6is %06ius "
"data: 0x%08X func: 0x%08X\n",
t->name,
t->tv_set.tv_jiff,
t->tv_set.tv_usec,
t->tv_expires.tv_jiff,
t->tv_expires.tv_usec,
t->data,
t->function
);
printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n",
t->delay_us,
tv_exp[j].tv_jiff,
tv_exp[j].tv_usec,
exp_num[j],
(tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) *
1000000 + tv_exp[j].tv_usec -
t->tv_expires.tv_usec);
}
proc_fasttimer_read(buf5, NULL, 0, 0, 0);
printk("buf5 after all done:\n");
printk(buf5);
printk("fast_timer_test() done\n");
}
#endif
int fast_timer_init(void)
{
/* For some reason, request_irq() hangs when called froom time_init() */
if (!fast_timer_is_init)
{
printk("fast_timer_init()\n");
#ifdef CONFIG_PROC_FS
proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
#endif /* PROC_FS */
if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt,
IRQF_SHARED,
"fast timer int", &fast_timer_list))
printk(KERN_ERR "err: fasttimer irq\n");
fast_timer_is_init = 1;
#ifdef FAST_TIMER_TEST
printk("do test\n");
fast_timer_test();
#endif
}
return 0;
}
__initcall(fast_timer_init);

View file

@ -0,0 +1,472 @@
/*
* CRISv32 kernel startup code.
*
* Copyright (C) 2003, Axis Communications AB
*/
#define ASSEMBLER_MACROS_ONLY
/*
* The macros found in mmu_defs_asm.h uses the ## concatenation operator, so
* -traditional must not be used when assembling this file.
*/
#include <arch/memmap.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/intr_vect.h>
#include <hwregs/asm/mmu_defs_asm.h>
#include <hwregs/asm/reg_map_asm.h>
#include <mach/startup.inc>
#define CRAMFS_MAGIC 0x28cd3d45
#define JHEAD_MAGIC 0x1FF528A6
#define JHEAD_SIZE 8
#define RAM_INIT_MAGIC 0x56902387
#define COMMAND_LINE_MAGIC 0x87109563
#define NAND_BOOT_MAGIC 0x9a9db001
;; NOTE: R8 and R9 carry information from the decompressor (if the
;; kernel was compressed). They must not be used in the code below
;; until they are read!
;; Exported symbols.
.global etrax_irv
.global romfs_start
.global romfs_length
.global romfs_in_flash
.global nand_boot
.global swapper_pg_dir
.text
tstart:
;; This is the entry point of the kernel. The CPU is currently in
;; supervisor mode.
;;
;; 0x00000000 if flash.
;; 0x40004000 if DRAM.
;;
di
START_CLOCKS
SETUP_WAIT_STATES
GIO_INIT
#ifdef CONFIG_SMP
secondary_cpu_entry: /* Entry point for secondary CPUs */
di
#endif
;; Setup and enable the MMU. Use same configuration for both the data
;; and the instruction MMU.
;;
;; Note; 3 cycles is needed for a bank-select to take effect. Further;
;; bank 1 is the instruction MMU, bank 2 is the data MMU.
#ifdef CONFIG_CRIS_MACH_ARTPEC3
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 5) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
#else
move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
#endif
;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00.
move.d REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4) \
| REG_FIELD(mmu, rw_mm_kbase_lo, base_0, 0), $r1
;; Enable certain page protections and setup linear mapping
;; for f,e,c,b,4,0.
;; ARTPEC-3:
;; c,d used for linear kernel mapping, up to 512 MB
;; e used for vmalloc
;; f unused, but page mapped to get page faults
;; ETRAX FS:
;; c used for linear kernel mapping, up to 256 MB
;; d used for vmalloc
;; e,f used for memory-mapped NOR flash
#ifdef CONFIG_CRIS_MACH_ARTPEC3
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
| REG_STATE(mmu, rw_mm_cfg, seg_f, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_e, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_d, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
#else
move.d REG_STATE(mmu, rw_mm_cfg, we, on) \
| REG_STATE(mmu, rw_mm_cfg, acc, on) \
| REG_STATE(mmu, rw_mm_cfg, ex, on) \
| REG_STATE(mmu, rw_mm_cfg, inv, on) \
| REG_STATE(mmu, rw_mm_cfg, seg_f, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_e, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_d, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_c, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_b, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_a, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_9, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_8, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_7, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_6, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_5, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_4, linear) \
| REG_STATE(mmu, rw_mm_cfg, seg_3, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_2, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_1, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
#endif
;; Update instruction MMU.
move 1, $srs
nop
nop
nop
move $r0, $s2 ; kbase_hi.
move $r1, $s1 ; kbase_lo.
move $r2, $s0 ; mm_cfg, virtual memory configuration.
;; Update data MMU.
move 2, $srs
nop
nop
nop
move $r0, $s2 ; kbase_hi.
move $r1, $s1 ; kbase_lo
move $r2, $s0 ; mm_cfg, virtual memory configuration.
;; Enable data and instruction MMU.
move 0, $srs
moveq 0xf, $r0 ; IMMU, DMMU, DCache, Icache on
nop
nop
nop
move $r0, $s0
nop
nop
nop
#ifdef CONFIG_SMP
;; Read CPU ID
move 0, $srs
nop
nop
nop
move $s12, $r0
cmpq 0, $r0
beq master_cpu
nop
slave_cpu:
; Time to boot-up. Get stack location provided by master CPU.
move.d smp_init_current_idle_thread, $r1
move.d [$r1], $sp
add.d 8192, $sp
move.d ebp_start, $r0 ; Defined in linker-script.
move $r0, $ebp
jsr smp_callin
nop
master_cpu:
/* Set up entry point for secondary CPUs. The boot ROM has set up
* EBP at start of internal memory. The CPU will get there
* later when we issue an IPI to them... */
move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0
move.d secondary_cpu_entry, $r1
move.d $r1, [$r0]
#endif
; Check if starting from DRAM (network->RAM boot or unpacked
; compressed kernel), or directly from flash.
lapcq ., $r0
and.d 0x7fffffff, $r0 ; Mask off the non-cache bit.
cmp.d 0x10000, $r0 ; Arbitrary, something above this code.
blo _inflash0
nop
jump _inram ; Jump to cached RAM.
nop
;; Jumpgate.
_inflash0:
jump _inflash
nop
;; Put the following in a section so that storage for it can be
;; reclaimed after init is finished.
.section ".init.text", "ax"
_inflash:
;; Initialize DRAM.
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
beq _dram_initialized
nop
#if defined CONFIG_ETRAXFS
#include "../mach-fs/dram_init.S"
#elif defined CONFIG_CRIS_MACH_ARTPEC3
#include "../mach-a3/dram_init.S"
#else
#error Only ETRAXFS and ARTPEC-3 supported!
#endif
_dram_initialized:
;; Copy the text and data section to DRAM. This depends on that the
;; variables used below are correctly set up by the linker script.
;; The calculated value stored in R4 is used below.
;; Leave the cramfs file system (piggybacked after the kernel) in flash.
moveq 0, $r0 ; Source.
move.d text_start, $r1 ; Destination.
move.d __vmlinux_end, $r2
move.d $r2, $r4
sub.d $r1, $r4
1: move.w [$r0+], $r3
move.w $r3, [$r1+]
cmp.d $r2, $r1
blo 1b
nop
;; Check for cramfs.
moveq 0, $r0
move.d romfs_length, $r1
move.d $r0, [$r1]
move.d [$r4], $r0 ; cramfs_super.magic
cmp.d CRAMFS_MAGIC, $r0
bne 1f
nop
;; Set length and start of cramfs, set romfs_in_flash flag
addoq +4, $r4, $acr
move.d [$acr], $r0
move.d romfs_length, $r1
move.d $r0, [$r1]
add.d 0xf0000000, $r4 ; Add cached flash start in virtual memory.
move.d romfs_start, $r1
move.d $r4, [$r1]
1: moveq 1, $r0
move.d romfs_in_flash, $r1
move.d $r0, [$r1]
jump _start_it ; Jump to cached code.
nop
_inram:
;; Check if booting from NAND flash; if so, set appropriate flags
;; and move on.
cmp.d NAND_BOOT_MAGIC, $r12
bne move_cramfs ; not nand, jump
moveq 1, $r0
move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND
move.d $r0, [$r1]
moveq 0, $r0 ; tell axisflashmap romfs is not in
move.d romfs_in_flash, $r1 ; (directly accessed) flash
move.d $r0, [$r1]
jump _start_it ; continue with boot
nop
move_cramfs:
;; kernel is in DRAM.
;; Must figure out if there is a piggybacked rootfs image or not.
;; Set romfs_length to 0 => no rootfs image available by default.
moveq 0, $r0
move.d romfs_length, $r1
move.d $r0, [$r1]
;; The kernel could have been unpacked to DRAM by the loader, but
;; the cramfs image could still be in the flash immediately
;; following the compressed kernel image. The loader passes the address
;; of the byte succeeding the last compressed byte in the flash in
;; register R9 when starting the kernel.
cmp.d 0x0ffffff8, $r9
bhs _no_romfs_in_flash ; R9 points outside the flash area.
nop
;; cramfs rootfs might to be in flash. Check for it.
move.d [$r9], $r0 ; cramfs_super.magic
cmp.d CRAMFS_MAGIC, $r0
bne _no_romfs_in_flash
nop
;; found cramfs in flash. set address and size, and romfs_in_flash flag.
addoq +4, $r9, $acr
move.d [$acr], $r0
move.d romfs_length, $r1
move.d $r0, [$r1]
add.d 0xf0000000, $r9 ; Add cached flash start in virtual memory.
move.d romfs_start, $r1
move.d $r9, [$r1]
moveq 1, $r0
move.d romfs_in_flash, $r1
move.d $r0, [$r1]
jump _start_it ; Jump to cached code.
nop
_no_romfs_in_flash:
;; No romfs in flash, so look for cramfs, or jffs2 with jhead,
;; after kernel in RAM, as is the case with network->RAM boot.
;; For cramfs, partition starts with magic and length.
;; For jffs2, a jhead is prepended which contains with magic and length.
;; The jhead is not part of the jffs2 partition however.
#ifndef CONFIG_ETRAXFS_SIM
move.d __bss_start, $r0
#else
move.d __end, $r0
#endif
move.d [$r0], $r1
cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic?
beq 2f ; yes, jump
nop
cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic?
bne 4f ; no, skip copy
nop
addq 4, $r0 ; location of jffs2 size
move.d [$r0+], $r2 ; fetch jffs2 size -> r2
; r0 now points to start of jffs2
ba 3f
nop
2:
addoq +4, $r0, $acr ; location of cramfs size
move.d [$acr], $r2 ; fetch cramfs size -> r2
; r0 still points to start of cramfs
3:
;; Now, move the root fs to after kernel's BSS
move.d _end, $r1 ; start of cramfs -> r1
move.d romfs_start, $r3
move.d $r1, [$r3] ; store at romfs_start (for axisflashmap)
move.d romfs_length, $r3
move.d $r2, [$r3] ; store size at romfs_length
add.d $r2, $r0 ; copy from end and downwards
add.d $r2, $r1
lsrq 1, $r2 ; Size is in bytes, we copy words.
addq 1, $r2
1:
move.w [$r0], $r3
move.w $r3, [$r1]
subq 2, $r0
subq 2, $r1
subq 1, $r2
bne 1b
nop
4:
;; BSS move done.
;; Clear romfs_in_flash flag, as we now know romfs is in DRAM
;; Also clear nand_boot flag; if we got here, we know we've not
;; booted from NAND flash.
moveq 0, $r0
move.d romfs_in_flash, $r1
move.d $r0, [$r1]
moveq 0, $r0
move.d nand_boot, $r1
move.d $r0, [$r1]
jump _start_it ; Jump to cached code.
nop
_start_it:
;; Check if kernel command line is supplied
cmp.d COMMAND_LINE_MAGIC, $r10
bne no_command_line
nop
move.d 256, $r13
move.d cris_command_line, $r10
or.d 0x80000000, $r11 ; Make it virtual
1:
move.b [$r11+], $r1
move.b $r1, [$r10+]
subq 1, $r13
bne 1b
nop
no_command_line:
;; The kernel stack contains a task structure for each task. This
;; the initial kernel stack is in the same page as the init_task,
;; but starts at the top of the page, i.e. + 8192 bytes.
move.d init_thread_union + 8192, $sp
move.d ebp_start, $r0 ; Defined in linker-script.
move $r0, $ebp
move.d etrax_irv, $r1 ; Set the exception base register and pointer.
move.d $r0, [$r1]
;; Clear the BSS region from _bss_start to _end.
move.d __bss_start, $r0
move.d _end, $r1
1: clear.d [$r0+]
cmp.d $r1, $r0
blo 1b
nop
; Initialize registers to increase determinism
move.d __bss_start, $r0
movem [$r0], $r13
#ifdef CONFIG_ETRAX_L2CACHE
jsr l2cache_init
nop
#endif
jump start_kernel ; Jump to start_kernel() in init/main.c.
nop
.data
etrax_irv:
.dword 0
; Variables for communication with the Axis flash map driver (axisflashmap),
; and for setting up memory in arch/cris/kernel/setup.c .
; romfs_start is set to the start of the root file system, if it exists
; in directly accessible memory (i.e. NOR Flash when booting from Flash,
; or RAM when booting directly from a network-downloaded RAM image)
romfs_start:
.dword 0
; romfs_length is set to the size of the root file system image, if it exists
; in directly accessible memory (see romfs_start). Otherwise it is set to 0.
romfs_length:
.dword 0
; romfs_in_flash is set to 1 if the root file system resides in directly
; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot
; or NAND flash boot.
romfs_in_flash:
.dword 0
; nand_boot is set to 1 when the kernel has been booted from NAND flash
nand_boot:
.dword 0
swapper_pg_dir = 0xc0002000
.section ".init.data", "aw"
#if defined CONFIG_ETRAXFS
#include "../mach-fs/hw_settings.S"
#elif defined CONFIG_CRIS_MACH_ARTPEC3
#include "../mach-a3/hw_settings.S"
#else
#error Only ETRAXFS and ARTPEC-3 supported!
#endif

View file

@ -0,0 +1,501 @@
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/profile.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/threads.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/intr_vect.h>
#include <hwregs/intr_vect_defs.h>
#define CPU_FIXED -1
/* IRQ masks (refer to comment for crisv32_do_multiple) */
#if TIMER0_INTR_VECT - FIRST_IRQ < 32
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ))
#undef TIMER_VECT1
#else
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32))
#define TIMER_VECT1
#endif
#ifdef CONFIG_ETRAX_KGDB
#if defined(CONFIG_ETRAX_KGDB_PORT0)
#define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ))
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
#define IGNOREMASK (1 << (SER1_INTR_VECT - FIRST_IRQ))
#elif defined(CONFIG_ETRAX_KGB_PORT2)
#define IGNOREMASK (1 << (SER2_INTR_VECT - FIRST_IRQ))
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
#define IGNOREMASK (1 << (SER3_INTR_VECT - FIRST_IRQ))
#endif
#endif
DEFINE_SPINLOCK(irq_lock);
struct cris_irq_allocation
{
int cpu; /* The CPU to which the IRQ is currently allocated. */
cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */
};
struct cris_irq_allocation irq_allocations[NR_REAL_IRQS] =
{ [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} };
static unsigned long irq_regs[NR_CPUS] =
{
regi_irq,
#ifdef CONFIG_SMP
regi_irq2,
#endif
};
#if NR_REAL_IRQS > 32
#define NBR_REGS 2
#else
#define NBR_REGS 1
#endif
unsigned long cpu_irq_counters[NR_CPUS];
unsigned long irq_counters[NR_REAL_IRQS];
/* From irq.c. */
extern void weird_irq(void);
/* From entry.S. */
extern void system_call(void);
extern void nmi_interrupt(void);
extern void multiple_interrupt(void);
extern void gdb_handle_exception(void);
extern void i_mmu_refill(void);
extern void i_mmu_invalid(void);
extern void i_mmu_access(void);
extern void i_mmu_execute(void);
extern void d_mmu_refill(void);
extern void d_mmu_invalid(void);
extern void d_mmu_access(void);
extern void d_mmu_write(void);
/* From kgdb.c. */
extern void kgdb_init(void);
extern void breakpoint(void);
/* From traps.c. */
extern void breakh_BUG(void);
/*
* Build the IRQ handler stubs using macros from irq.h.
*/
#ifdef CONFIG_CRIS_MACH_ARTPEC3
BUILD_TIMER_IRQ(0x31, 0)
#else
BUILD_IRQ(0x31)
#endif
BUILD_IRQ(0x32)
BUILD_IRQ(0x33)
BUILD_IRQ(0x34)
BUILD_IRQ(0x35)
BUILD_IRQ(0x36)
BUILD_IRQ(0x37)
BUILD_IRQ(0x38)
BUILD_IRQ(0x39)
BUILD_IRQ(0x3a)
BUILD_IRQ(0x3b)
BUILD_IRQ(0x3c)
BUILD_IRQ(0x3d)
BUILD_IRQ(0x3e)
BUILD_IRQ(0x3f)
BUILD_IRQ(0x40)
BUILD_IRQ(0x41)
BUILD_IRQ(0x42)
BUILD_IRQ(0x43)
BUILD_IRQ(0x44)
BUILD_IRQ(0x45)
BUILD_IRQ(0x46)
BUILD_IRQ(0x47)
BUILD_IRQ(0x48)
BUILD_IRQ(0x49)
BUILD_IRQ(0x4a)
#ifdef CONFIG_ETRAXFS
BUILD_TIMER_IRQ(0x4b, 0)
#else
BUILD_IRQ(0x4b)
#endif
BUILD_IRQ(0x4c)
BUILD_IRQ(0x4d)
BUILD_IRQ(0x4e)
BUILD_IRQ(0x4f)
BUILD_IRQ(0x50)
#if MACH_IRQS > 32
BUILD_IRQ(0x51)
BUILD_IRQ(0x52)
BUILD_IRQ(0x53)
BUILD_IRQ(0x54)
BUILD_IRQ(0x55)
BUILD_IRQ(0x56)
BUILD_IRQ(0x57)
BUILD_IRQ(0x58)
BUILD_IRQ(0x59)
BUILD_IRQ(0x5a)
BUILD_IRQ(0x5b)
BUILD_IRQ(0x5c)
BUILD_IRQ(0x5d)
BUILD_IRQ(0x5e)
BUILD_IRQ(0x5f)
BUILD_IRQ(0x60)
BUILD_IRQ(0x61)
BUILD_IRQ(0x62)
BUILD_IRQ(0x63)
BUILD_IRQ(0x64)
BUILD_IRQ(0x65)
BUILD_IRQ(0x66)
BUILD_IRQ(0x67)
BUILD_IRQ(0x68)
BUILD_IRQ(0x69)
BUILD_IRQ(0x6a)
BUILD_IRQ(0x6b)
BUILD_IRQ(0x6c)
BUILD_IRQ(0x6d)
BUILD_IRQ(0x6e)
BUILD_IRQ(0x6f)
BUILD_IRQ(0x70)
#endif
/* Pointers to the low-level handlers. */
static void (*interrupt[MACH_IRQS])(void) = {
IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt,
IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt,
IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt,
IRQ0x3a_interrupt, IRQ0x3b_interrupt, IRQ0x3c_interrupt,
IRQ0x3d_interrupt, IRQ0x3e_interrupt, IRQ0x3f_interrupt,
IRQ0x40_interrupt, IRQ0x41_interrupt, IRQ0x42_interrupt,
IRQ0x43_interrupt, IRQ0x44_interrupt, IRQ0x45_interrupt,
IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt,
IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt,
IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt,
IRQ0x4f_interrupt, IRQ0x50_interrupt,
#if MACH_IRQS > 32
IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt,
IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt,
IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt,
IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt,
IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt,
IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt,
IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt,
IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt,
IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt,
IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt,
IRQ0x6f_interrupt, IRQ0x70_interrupt,
#endif
};
void
block_irq(int irq, int cpu)
{
int intr_mask;
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
/* Remember, 1 let thru, 0 block. */
if (irq - FIRST_IRQ < 32) {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 0);
intr_mask &= ~(1 << (irq - FIRST_IRQ));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
0, intr_mask);
} else {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 1);
intr_mask &= ~(1 << (irq - FIRST_IRQ - 32));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
1, intr_mask);
}
spin_unlock_irqrestore(&irq_lock, flags);
}
void
unblock_irq(int irq, int cpu)
{
int intr_mask;
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
/* Remember, 1 let thru, 0 block. */
if (irq - FIRST_IRQ < 32) {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 0);
intr_mask |= (1 << (irq - FIRST_IRQ));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
0, intr_mask);
} else {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 1);
intr_mask |= (1 << (irq - FIRST_IRQ - 32));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
1, intr_mask);
}
spin_unlock_irqrestore(&irq_lock, flags);
}
/* Find out which CPU the irq should be allocated to. */
static int irq_cpu(int irq)
{
int cpu;
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
cpu = irq_allocations[irq - FIRST_IRQ].cpu;
/* Fixed interrupts stay on the local CPU. */
if (cpu == CPU_FIXED)
{
spin_unlock_irqrestore(&irq_lock, flags);
return smp_processor_id();
}
/* Let the interrupt stay if possible */
if (cpumask_test_cpu(cpu, &irq_allocations[irq - FIRST_IRQ].mask))
goto out;
/* IRQ must be moved to another CPU. */
cpu = cpumask_first(&irq_allocations[irq - FIRST_IRQ].mask);
irq_allocations[irq - FIRST_IRQ].cpu = cpu;
out:
spin_unlock_irqrestore(&irq_lock, flags);
return cpu;
}
void crisv32_mask_irq(int irq)
{
int cpu;
for (cpu = 0; cpu < NR_CPUS; cpu++)
block_irq(irq, cpu);
}
void crisv32_unmask_irq(int irq)
{
unblock_irq(irq, irq_cpu(irq));
}
static void enable_crisv32_irq(struct irq_data *data)
{
crisv32_unmask_irq(data->irq);
}
static void disable_crisv32_irq(struct irq_data *data)
{
crisv32_mask_irq(data->irq);
}
static int set_affinity_crisv32_irq(struct irq_data *data,
const struct cpumask *dest, bool force)
{
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
irq_allocations[data->irq - FIRST_IRQ].mask = *dest;
spin_unlock_irqrestore(&irq_lock, flags);
return 0;
}
static struct irq_chip crisv32_irq_type = {
.name = "CRISv32",
.irq_shutdown = disable_crisv32_irq,
.irq_enable = enable_crisv32_irq,
.irq_disable = disable_crisv32_irq,
.irq_set_affinity = set_affinity_crisv32_irq,
};
void
set_exception_vector(int n, irqvectptr addr)
{
etrax_irv->v[n] = (irqvectptr) addr;
}
extern void do_IRQ(int irq, struct pt_regs * regs);
void
crisv32_do_IRQ(int irq, int block, struct pt_regs* regs)
{
/* Interrupts that may not be moved to another CPU may
* skip blocking. This is currently only valid for the
* timer IRQ and the IPI and is used for the timer
* interrupt to avoid watchdog starvation.
*/
if (!block) {
do_IRQ(irq, regs);
return;
}
block_irq(irq, smp_processor_id());
do_IRQ(irq, regs);
unblock_irq(irq, irq_cpu(irq));
}
/* If multiple interrupts occur simultaneously we get a multiple
* interrupt from the CPU and software has to sort out which
* interrupts that happened. There are two special cases here:
*
* 1. Timer interrupts may never be blocked because of the
* watchdog (refer to comment in include/asr/arch/irq.h)
* 2. GDB serial port IRQs are unhandled here and will be handled
* as a single IRQ when it strikes again because the GDB
* stubb wants to save the registers in its own fashion.
*/
void
crisv32_do_multiple(struct pt_regs* regs)
{
int cpu;
int mask;
int masked[NBR_REGS];
int bit;
int i;
cpu = smp_processor_id();
/* An extra irq_enter here to prevent softIRQs to run after
* each do_IRQ. This will decrease the interrupt latency.
*/
irq_enter();
for (i = 0; i < NBR_REGS; i++) {
/* Get which IRQs that happened. */
masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
r_masked_vect, i);
/* Calculate new IRQ mask with these IRQs disabled. */
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
mask &= ~masked[i];
/* Timer IRQ is never masked */
#ifdef TIMER_VECT1
if ((i == 1) && (masked[0] & TIMER_MASK))
mask |= TIMER_MASK;
#else
if ((i == 0) && (masked[0] & TIMER_MASK))
mask |= TIMER_MASK;
#endif
/* Block all the IRQs */
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
/* Check for timer IRQ and handle it special. */
#ifdef TIMER_VECT1
if ((i == 1) && (masked[i] & TIMER_MASK)) {
masked[i] &= ~TIMER_MASK;
do_IRQ(TIMER0_INTR_VECT, regs);
}
#else
if ((i == 0) && (masked[i] & TIMER_MASK)) {
masked[i] &= ~TIMER_MASK;
do_IRQ(TIMER0_INTR_VECT, regs);
}
#endif
}
#ifdef IGNORE_MASK
/* Remove IRQs that can't be handled as multiple. */
masked[0] &= ~IGNORE_MASK;
#endif
/* Handle the rest of the IRQs. */
for (i = 0; i < NBR_REGS; i++) {
for (bit = 0; bit < 32; bit++) {
if (masked[i] & (1 << bit))
do_IRQ(bit + FIRST_IRQ + i*32, regs);
}
}
/* Unblock all the IRQs. */
for (i = 0; i < NBR_REGS; i++) {
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
mask |= masked[i];
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
}
/* This irq_exit() will trigger the soft IRQs. */
irq_exit();
}
/*
* This is called by start_kernel. It fixes the IRQ masks and setup the
* interrupt vector table to point to bad_interrupt pointers.
*/
void __init
init_IRQ(void)
{
int i;
int j;
reg_intr_vect_rw_mask vect_mask = {0};
/* Clear all interrupts masks. */
for (i = 0; i < NBR_REGS; i++)
REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask);
for (i = 0; i < 256; i++)
etrax_irv->v[i] = weird_irq;
/* Point all IRQ's to bad handlers. */
for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) {
irq_set_chip_and_handler(j, &crisv32_irq_type,
handle_simple_irq);
set_exception_vector(i, interrupt[j]);
}
/* Mark Timer and IPI IRQs as CPU local */
irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
irq_set_status_flags(TIMER0_INTR_VECT, IRQ_PER_CPU);
irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
irq_set_status_flags(IPI_INTR_VECT, IRQ_PER_CPU);
set_exception_vector(0x00, nmi_interrupt);
set_exception_vector(0x30, multiple_interrupt);
/* Set up handler for various MMU bus faults. */
set_exception_vector(0x04, i_mmu_refill);
set_exception_vector(0x05, i_mmu_invalid);
set_exception_vector(0x06, i_mmu_access);
set_exception_vector(0x07, i_mmu_execute);
set_exception_vector(0x08, d_mmu_refill);
set_exception_vector(0x09, d_mmu_invalid);
set_exception_vector(0x0a, d_mmu_access);
set_exception_vector(0x0b, d_mmu_write);
#ifdef CONFIG_BUG
/* Break 14 handler, used to implement cheap BUG(). */
set_exception_vector(0x1e, breakh_BUG);
#endif
/* The system-call trap is reached by "break 13". */
set_exception_vector(0x1d, system_call);
/* Exception handlers for debugging, both user-mode and kernel-mode. */
/* Break 8. */
set_exception_vector(0x18, gdb_handle_exception);
/* Hardware single step. */
set_exception_vector(0x3, gdb_handle_exception);
/* Hardware breakpoint. */
set_exception_vector(0xc, gdb_handle_exception);
#ifdef CONFIG_ETRAX_KGDB
kgdb_init();
/* Everything is set up; now trap the kernel. */
breakpoint();
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,551 @@
/*
* Copyright (C) 2004 Axis Communications AB
*
* Code for handling break 8, hardware breakpoint, single step, and serial
* port exceptions for kernel debugging purposes.
*/
#include <hwregs/intr_vect.h>
;; Exported functions.
.globl kgdb_handle_exception
kgdb_handle_exception:
;; Create a register image of the caller.
;;
;; First of all, save the ACR on the stack since we need it for address calculations.
;; We put it into the register struct later.
subq 4, $sp
move.d $acr, [$sp]
;; Now we are free to use ACR all we want.
;; If we were running this handler with interrupts on, we would have to be careful
;; to save and restore CCS manually, but since we aren't we treat it like every other
;; register.
move.d reg, $acr
move.d $r0, [$acr] ; Save R0 (start of register struct)
addq 4, $acr
move.d $r1, [$acr] ; Save R1
addq 4, $acr
move.d $r2, [$acr] ; Save R2
addq 4, $acr
move.d $r3, [$acr] ; Save R3
addq 4, $acr
move.d $r4, [$acr] ; Save R4
addq 4, $acr
move.d $r5, [$acr] ; Save R5
addq 4, $acr
move.d $r6, [$acr] ; Save R6
addq 4, $acr
move.d $r7, [$acr] ; Save R7
addq 4, $acr
move.d $r8, [$acr] ; Save R8
addq 4, $acr
move.d $r9, [$acr] ; Save R9
addq 4, $acr
move.d $r10, [$acr] ; Save R10
addq 4, $acr
move.d $r11, [$acr] ; Save R11
addq 4, $acr
move.d $r12, [$acr] ; Save R12
addq 4, $acr
move.d $r13, [$acr] ; Save R13
addq 4, $acr
move.d $sp, [$acr] ; Save SP (R14)
addq 4, $acr
;; The ACR register is already saved on the stack, so pop it from there.
move.d [$sp],$r0
move.d $r0, [$acr]
addq 4, $acr
move $bz, [$acr]
addq 1, $acr
move $vr, [$acr]
addq 1, $acr
move $pid, [$acr]
addq 4, $acr
move $srs, [$acr]
addq 1, $acr
move $wz, [$acr]
addq 2, $acr
move $exs, [$acr]
addq 4, $acr
move $eda, [$acr]
addq 4, $acr
move $mof, [$acr]
addq 4, $acr
move $dz, [$acr]
addq 4, $acr
move $ebp, [$acr]
addq 4, $acr
move $erp, [$acr]
addq 4, $acr
move $srp, [$acr]
addq 4, $acr
move $nrp, [$acr]
addq 4, $acr
move $ccs, [$acr]
addq 4, $acr
move $usp, [$acr]
addq 4, $acr
move $spc, [$acr]
addq 4, $acr
;; Skip the pseudo-PC.
addq 4, $acr
;; Save the support registers in bank 0 - 3.
clear.d $r1 ; Bank counter
move.d sreg, $acr
;; Bank 0
move $r1, $srs
nop
nop
nop
move $s0, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s1, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s2, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s3, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s4, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s5, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s6, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s7, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s8, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s9, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s10, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s11, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s12, $r0
move.d $r0, [$acr]
addq 4, $acr
;; Nothing in S13 - S15, bank 0
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
;; Bank 1 and bank 2 have the same layout, hence the loop.
addq 1, $r1
1:
move $r1, $srs
nop
nop
nop
move $s0, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s1, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s2, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s3, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s4, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s5, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s6, $r0
move.d $r0, [$acr]
addq 4, $acr
;; Nothing in S7 - S15, bank 1 and 2
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
clear.d [$acr]
addq 4, $acr
addq 1, $r1
cmpq 3, $r1
bne 1b
nop
;; Bank 3
move $r1, $srs
nop
nop
nop
move $s0, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s1, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s2, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s3, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s4, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s5, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s6, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s7, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s8, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s9, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s10, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s11, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s12, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s13, $r0
move.d $r0, [$acr]
addq 4, $acr
move $s14, $r0
move.d $r0, [$acr]
addq 4, $acr
;; Nothing in S15, bank 3
clear.d [$acr]
addq 4, $acr
;; Check what got us here: get IDX field of EXS.
move $exs, $r10
and.d 0xff00, $r10
lsrq 8, $r10
#if defined(CONFIG_ETRAX_KGDB_PORT0)
cmp.d SER0_INTR_VECT, $r10 ; IRQ for serial port 0
beq sigint
nop
#elif defined(CONFIG_ETRAX_KGDB_PORT1)
cmp.d SER1_INTR_VECT, $r10 ; IRQ for serial port 1
beq sigint
nop
#elif defined(CONFIG_ETRAX_KGDB_PORT2)
cmp.d SER2_INTR_VECT, $r10 ; IRQ for serial port 2
beq sigint
nop
#elif defined(CONFIG_ETRAX_KGDB_PORT3)
cmp.d SER3_INTR_VECT, $r10 ; IRQ for serial port 3
beq sigint
nop
#endif
;; Multiple interrupt must be due to serial break.
cmp.d 0x30, $r10 ; Multiple interrupt
beq sigint
nop
;; Neither of those? Then it's a sigtrap.
ba handle_comm
moveq 5, $r10 ; Set SIGTRAP (delay slot)
sigint:
;; Serial interrupt; get character
jsr getDebugChar
nop ; Delay slot
cmp.b 3, $r10 ; \003 (Ctrl-C)?
bne return ; No, get out of here
nop
moveq 2, $r10 ; Set SIGINT
;;
;; Handle the communication
;;
handle_comm:
move.d internal_stack+1020, $sp ; Use the internal stack which grows upwards
jsr handle_exception ; Interactive routine
nop
;;
;; Return to the caller
;;
return:
;; First of all, write the support registers.
clear.d $r1 ; Bank counter
move.d sreg, $acr
;; Bank 0
move $r1, $srs
nop
nop
nop
move.d [$acr], $r0
move $r0, $s0
addq 4, $acr
move.d [$acr], $r0
move $r0, $s1
addq 4, $acr
move.d [$acr], $r0
move $r0, $s2
addq 4, $acr
move.d [$acr], $r0
move $r0, $s3
addq 4, $acr
move.d [$acr], $r0
move $r0, $s4
addq 4, $acr
move.d [$acr], $r0
move $r0, $s5
addq 4, $acr
;; Nothing in S6 - S7, bank 0.
addq 4, $acr
addq 4, $acr
move.d [$acr], $r0
move $r0, $s8
addq 4, $acr
move.d [$acr], $r0
move $r0, $s9
addq 4, $acr
move.d [$acr], $r0
move $r0, $s10
addq 4, $acr
move.d [$acr], $r0
move $r0, $s11
addq 4, $acr
move.d [$acr], $r0
move $r0, $s12
addq 4, $acr
;; Nothing in S13 - S15, bank 0
addq 4, $acr
addq 4, $acr
addq 4, $acr
;; Bank 1 and bank 2 have the same layout, hence the loop.
addq 1, $r1
2:
move $r1, $srs
nop
nop
nop
move.d [$acr], $r0
move $r0, $s0
addq 4, $acr
move.d [$acr], $r0
move $r0, $s1
addq 4, $acr
move.d [$acr], $r0
move $r0, $s2
addq 4, $acr
;; S3 (MM_CAUSE) is read-only.
addq 4, $acr
move.d [$acr], $r0
move $r0, $s4
addq 4, $acr
;; FIXME: Actually write S5/S6? (Affects MM_CAUSE.)
addq 4, $acr
addq 4, $acr
;; Nothing in S7 - S15, bank 1 and 2
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 4, $acr
addq 1, $r1
cmpq 3, $r1
bne 2b
nop
;; Bank 3
move $r1, $srs
nop
nop
nop
move.d [$acr], $r0
move $r0, $s0
addq 4, $acr
move.d [$acr], $r0
move $r0, $s1
addq 4, $acr
move.d [$acr], $r0
move $r0, $s2
addq 4, $acr
move.d [$acr], $r0
move $r0, $s3
addq 4, $acr
move.d [$acr], $r0
move $r0, $s4
addq 4, $acr
move.d [$acr], $r0
move $r0, $s5
addq 4, $acr
move.d [$acr], $r0
move $r0, $s6
addq 4, $acr
move.d [$acr], $r0
move $r0, $s7
addq 4, $acr
move.d [$acr], $r0
move $r0, $s8
addq 4, $acr
move.d [$acr], $r0
move $r0, $s9
addq 4, $acr
move.d [$acr], $r0
move $r0, $s10
addq 4, $acr
move.d [$acr], $r0
move $r0, $s11
addq 4, $acr
move.d [$acr], $r0
move $r0, $s12
addq 4, $acr
move.d [$acr], $r0
move $r0, $s13
addq 4, $acr
move.d [$acr], $r0
move $r0, $s14
addq 4, $acr
;; Nothing in S15, bank 3
addq 4, $acr
;; Now, move on to the regular register restoration process.
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
move.d [$acr], $r0 ; Restore R0
addq 4, $acr
move.d [$acr], $r1 ; Restore R1
addq 4, $acr
move.d [$acr], $r2 ; Restore R2
addq 4, $acr
move.d [$acr], $r3 ; Restore R3
addq 4, $acr
move.d [$acr], $r4 ; Restore R4
addq 4, $acr
move.d [$acr], $r5 ; Restore R5
addq 4, $acr
move.d [$acr], $r6 ; Restore R6
addq 4, $acr
move.d [$acr], $r7 ; Restore R7
addq 4, $acr
move.d [$acr], $r8 ; Restore R8
addq 4, $acr
move.d [$acr], $r9 ; Restore R9
addq 4, $acr
move.d [$acr], $r10 ; Restore R10
addq 4, $acr
move.d [$acr], $r11 ; Restore R11
addq 4, $acr
move.d [$acr], $r12 ; Restore R12
addq 4, $acr
move.d [$acr], $r13 ; Restore R13
;;
;; We restore all registers, even though some of them probably haven't changed.
;;
addq 4, $acr
move.d [$acr], $sp ; Restore SP (R14)
;; ACR cannot be restored just yet.
addq 8, $acr
;; Skip BZ, VR.
addq 2, $acr
move [$acr], $pid ; Restore PID
addq 4, $acr
move [$acr], $srs ; Restore SRS
nop
nop
nop
addq 1, $acr
;; Skip WZ.
addq 2, $acr
move [$acr], $exs ; Restore EXS.
addq 4, $acr
move [$acr], $eda ; Restore EDA.
addq 4, $acr
move [$acr], $mof ; Restore MOF.
;; Skip DZ.
addq 8, $acr
move [$acr], $ebp ; Restore EBP.
addq 4, $acr
move [$acr], $erp ; Restore ERP.
addq 4, $acr
move [$acr], $srp ; Restore SRP.
addq 4, $acr
move [$acr], $nrp ; Restore NRP.
addq 4, $acr
move [$acr], $ccs ; Restore CCS like an ordinary register.
addq 4, $acr
move [$acr], $usp ; Restore USP
addq 4, $acr
move [$acr], $spc ; Restore SPC
; No restoration of pseudo-PC of course.
move.d reg, $acr ; Reset ACR to point at the beginning of the register image
add.d 15*4, $acr
move.d [$acr], $acr ; Finally, restore ACR.
rete ; Same as jump ERP
rfe ; Shifts CCS

View file

@ -0,0 +1,184 @@
/*
* Copyright (C) 2000-2003 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
* Mikael Starvik (starvik@axis.com)
* Tobias Anderberg (tobiasa@axis.com), CRISv32 port.
*
* This file handles the architecture-dependent parts of process handling..
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/timer_defs.h>
#include <hwregs/intr_vect_defs.h>
#include <linux/ptrace.h>
extern void stop_watchdog(void);
/* We use this if we don't have any better idle routine. */
void default_idle(void)
{
/* Halt until exception. */
__asm__ volatile("ei \n\t"
"halt ");
}
/*
* Free current thread data structures etc..
*/
extern void deconfigure_bp(long pid);
void exit_thread(void)
{
deconfigure_bp(current->pid);
}
/*
* If the watchdog is enabled, disable interrupts and enter an infinite loop.
* The watchdog will reset the CPU after 0.1s. If the watchdog isn't enabled
* then enable it and wait.
*/
extern void arch_enable_nmi(void);
void
hard_reset_now(void)
{
/*
* Don't declare this variable elsewhere. We don't want any other
* code to know about it than the watchdog handler in entry.S and
* this code, implementing hard reset through the watchdog.
*/
#if defined(CONFIG_ETRAX_WATCHDOG)
extern int cause_of_death;
#endif
printk("*** HARD RESET ***\n");
local_irq_disable();
#if defined(CONFIG_ETRAX_WATCHDOG)
cause_of_death = 0xbedead;
#else
{
reg_timer_rw_wd_ctrl wd_ctrl = {0};
stop_watchdog();
wd_ctrl.key = 16; /* Arbitrary key. */
wd_ctrl.cnt = 1; /* Minimum time. */
wd_ctrl.cmd = regk_timer_start;
arch_enable_nmi();
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
}
#endif
while (1)
; /* Wait for reset. */
}
/*
* Return saved PC of a blocked thread.
*/
unsigned long thread_saved_pc(struct task_struct *t)
{
return task_pt_regs(t)->erp;
}
/*
* Setup the child's kernel stack with a pt_regs and call switch_stack() on it.
* It will be unnested during _resume and _ret_from_sys_call when the new thread
* is scheduled.
*
* Also setup the thread switching structure which is used to keep
* thread-specific data during _resumes.
*/
extern asmlinkage void ret_from_fork(void);
extern asmlinkage void ret_from_kernel_thread(void);
int
copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long arg, struct task_struct *p)
{
struct pt_regs *childregs = task_pt_regs(p);
struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
/*
* Put the pt_regs structure at the end of the new kernel stack page and
* fix it up. Note: the task_struct doubles as the kernel stack for the
* task.
*/
if (unlikely(p->flags & PF_KTHREAD)) {
memset(swstack, 0,
sizeof(struct switch_stack) + sizeof(struct pt_regs));
swstack->r1 = usp;
swstack->r2 = arg;
childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
p->thread.ksp = (unsigned long) swstack;
p->thread.usp = 0;
return 0;
}
*childregs = *current_pt_regs(); /* Struct copy of pt_regs. */
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
/* Set a new TLS ?
* The TLS is in $mof because it is the 5th argument to sys_clone.
*/
if (p->mm && (clone_flags & CLONE_SETTLS)) {
task_thread_info(p)->tls = childregs->mof;
}
/* Put the switch stack right below the pt_regs. */
/* Parameter to ret_from_sys_call. 0 is don't restart the syscall. */
swstack->r9 = 0;
/*
* We want to return into ret_from_sys_call after the _resume.
* ret_from_fork will call ret_from_sys_call.
*/
swstack->return_ip = (unsigned long) ret_from_fork;
/* Fix the user-mode and kernel-mode stackpointer. */
p->thread.usp = usp ?: rdusp();
p->thread.ksp = (unsigned long) swstack;
return 0;
}
unsigned long
get_wchan(struct task_struct *p)
{
/* TODO */
return 0;
}
#undef last_sched
#undef first_sched
void show_regs(struct pt_regs * regs)
{
unsigned long usp = rdusp();
show_regs_print_info(KERN_DEFAULT);
printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
regs->r0, regs->r1, regs->r2, regs->r3);
printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
regs->r4, regs->r5, regs->r6, regs->r7);
printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
regs->r8, regs->r9, regs->r10, regs->r11);
printk("r12: %08lx r13: %08lx oR10: %08lx\n",
regs->r12, regs->r13, regs->orig_r10);
}

View file

@ -0,0 +1,490 @@
/*
* Copyright (C) 2000-2007, Axis Communications AB.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/signal.h>
#include <linux/security.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <arch/hwregs/supp_reg.h>
/*
* Determines which bits in CCS the user has access to.
* 1 = access, 0 = no access.
*/
#define CCS_MASK 0x00087c00 /* SXNZVC */
#define SBIT_USER (1 << (S_CCS_BITNR + CCS_SHIFT))
static int put_debugreg(long pid, unsigned int regno, long data);
static long get_debugreg(long pid, unsigned int regno);
static unsigned long get_pseudo_pc(struct task_struct *child);
void deconfigure_bp(long pid);
extern unsigned long cris_signal_return_page;
/*
* Get contents of register REGNO in task TASK.
*/
long get_reg(struct task_struct *task, unsigned int regno)
{
/* USP is a special case, it's not in the pt_regs struct but
* in the tasks thread struct
*/
unsigned long ret;
if (regno <= PT_EDA)
ret = ((unsigned long *)task_pt_regs(task))[regno];
else if (regno == PT_USP)
ret = task->thread.usp;
else if (regno == PT_PPC)
ret = get_pseudo_pc(task);
else if (regno <= PT_MAX)
ret = get_debugreg(task->pid, regno);
else
ret = 0;
return ret;
}
/*
* Write contents of register REGNO in task TASK.
*/
int put_reg(struct task_struct *task, unsigned int regno, unsigned long data)
{
if (regno <= PT_EDA)
((unsigned long *)task_pt_regs(task))[regno] = data;
else if (regno == PT_USP)
task->thread.usp = data;
else if (regno == PT_PPC) {
/* Write pseudo-PC to ERP only if changed. */
if (data != get_pseudo_pc(task))
task_pt_regs(task)->erp = data;
} else if (regno <= PT_MAX)
return put_debugreg(task->pid, regno, data);
else
return -1;
return 0;
}
void user_enable_single_step(struct task_struct *child)
{
unsigned long tmp;
/*
* Set up SPC if not set already (in which case we have no other
* choice but to trust it).
*/
if (!get_reg(child, PT_SPC)) {
/* In case we're stopped in a delay slot. */
tmp = get_reg(child, PT_ERP) & ~1;
put_reg(child, PT_SPC, tmp);
}
tmp = get_reg(child, PT_CCS) | SBIT_USER;
put_reg(child, PT_CCS, tmp);
}
void user_disable_single_step(struct task_struct *child)
{
put_reg(child, PT_SPC, 0);
if (!get_debugreg(child->pid, PT_BP_CTRL)) {
unsigned long tmp;
/* If no h/w bp configured, disable S bit. */
tmp = get_reg(child, PT_CCS) & ~SBIT_USER;
put_reg(child, PT_CCS, tmp);
}
}
/*
* Called by kernel/ptrace.c when detaching.
*
* Make sure the single step bit is not set.
*/
void
ptrace_disable(struct task_struct *child)
{
/* Deconfigure SPC and S-bit. */
user_disable_single_step(child);
put_reg(child, PT_SPC, 0);
/* Deconfigure any watchpoints associated with the child. */
deconfigure_bp(child->pid);
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret;
unsigned int regno = addr >> 2;
unsigned long __user *datap = (unsigned long __user *)data;
switch (request) {
/* Read word at location address. */
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA: {
unsigned long tmp;
int copied;
ret = -EIO;
/* The signal trampoline page is outside the normal user-addressable
* space but still accessible. This is hack to make it possible to
* access the signal handler code in GDB.
*/
if ((addr & PAGE_MASK) == cris_signal_return_page) {
/* The trampoline page is globally mapped, no page table to traverse.*/
tmp = *(unsigned long*)addr;
} else {
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
if (copied != sizeof(tmp))
break;
}
ret = put_user(tmp,datap);
break;
}
/* Read the word at location address in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & 3) || regno > PT_MAX)
break;
tmp = get_reg(child, regno);
ret = put_user(tmp, datap);
break;
}
/* Write the word at location address. */
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
ret = generic_ptrace_pokedata(child, addr, data);
break;
/* Write the word at location address in the USER area. */
case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || regno > PT_MAX)
break;
if (regno == PT_CCS) {
/* don't allow the tracing process to change stuff like
* interrupt enable, kernel/user bit, dma enables etc.
*/
data &= CCS_MASK;
data |= get_reg(child, PT_CCS) & ~CCS_MASK;
}
if (put_reg(child, regno, data))
break;
ret = 0;
break;
/* Get all GP registers from the child. */
case PTRACE_GETREGS: {
int i;
unsigned long tmp;
for (i = 0; i <= PT_MAX; i++) {
tmp = get_reg(child, i);
if (put_user(tmp, datap)) {
ret = -EFAULT;
goto out_tsk;
}
datap++;
}
ret = 0;
break;
}
/* Set all GP registers in the child. */
case PTRACE_SETREGS: {
int i;
unsigned long tmp;
for (i = 0; i <= PT_MAX; i++) {
if (get_user(tmp, datap)) {
ret = -EFAULT;
goto out_tsk;
}
if (i == PT_CCS) {
tmp &= CCS_MASK;
tmp |= get_reg(child, PT_CCS) & ~CCS_MASK;
}
put_reg(child, i, tmp);
datap++;
}
ret = 0;
break;
}
default:
ret = ptrace_request(child, request, addr, data);
break;
}
out_tsk:
return ret;
}
void do_syscall_trace(void)
{
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0));
/*
* This isn't the same as continuing with a signal, but it will do for
* normal use.
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
}
/* Returns the size of an instruction that has a delay slot. */
static int insn_size(struct task_struct *child, unsigned long pc)
{
unsigned long opcode;
int copied;
int opsize = 0;
/* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
if (copied != sizeof(opcode))
return 0;
switch ((opcode & 0x0f00) >> 8) {
case 0x0:
case 0x9:
case 0xb:
opsize = 2;
break;
case 0xe:
case 0xf:
opsize = 6;
break;
case 0xd:
/* Could be 4 or 6; check more bits. */
if ((opcode & 0xff) == 0xff)
opsize = 4;
else
opsize = 6;
break;
default:
panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n",
opcode, pc);
}
return opsize;
}
static unsigned long get_pseudo_pc(struct task_struct *child)
{
/* Default value for PC is ERP. */
unsigned long pc = get_reg(child, PT_ERP);
if (pc & 0x1) {
unsigned long spc = get_reg(child, PT_SPC);
/* Delay slot bit set. Report as stopped on proper
instruction. */
if (spc) {
/* Rely on SPC if set. FIXME: We might want to check
that EXS indicates we stopped due to a single-step
exception. */
pc = spc;
} else {
/* Calculate the PC from the size of the instruction
that the delay slot we're in belongs to. */
pc += insn_size(child, pc & ~1) - 1;
}
}
return pc;
}
static long bp_owner = 0;
/* Reachable from exit_thread in signal.c, so not static. */
void deconfigure_bp(long pid)
{
int bp;
/* Only deconfigure if the pid is the owner. */
if (bp_owner != pid)
return;
for (bp = 0; bp < 6; bp++) {
unsigned long tmp;
/* Deconfigure start and end address (also gets rid of ownership). */
put_debugreg(pid, PT_BP + 3 + (bp * 2), 0);
put_debugreg(pid, PT_BP + 4 + (bp * 2), 0);
/* Deconfigure relevant bits in control register. */
tmp = get_debugreg(pid, PT_BP_CTRL) & ~(3 << (2 + (bp * 4)));
put_debugreg(pid, PT_BP_CTRL, tmp);
}
/* No owner now. */
bp_owner = 0;
}
static int put_debugreg(long pid, unsigned int regno, long data)
{
int ret = 0;
register int old_srs;
#ifdef CONFIG_ETRAX_KGDB
/* Ignore write, but pretend it was ok if value is 0
(we don't want POKEUSR/SETREGS failing unnessecarily). */
return (data == 0) ? ret : -1;
#endif
/* Simple owner management. */
if (!bp_owner)
bp_owner = pid;
else if (bp_owner != pid) {
/* Ignore write, but pretend it was ok if value is 0
(we don't want POKEUSR/SETREGS failing unnessecarily). */
return (data == 0) ? ret : -1;
}
/* Remember old SRS. */
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
/* Switch to BP bank. */
SUPP_BANK_SEL(BANK_BP);
switch (regno - PT_BP) {
case 0:
SUPP_REG_WR(0, data); break;
case 1:
case 2:
if (data)
ret = -1;
break;
case 3:
SUPP_REG_WR(3, data); break;
case 4:
SUPP_REG_WR(4, data); break;
case 5:
SUPP_REG_WR(5, data); break;
case 6:
SUPP_REG_WR(6, data); break;
case 7:
SUPP_REG_WR(7, data); break;
case 8:
SUPP_REG_WR(8, data); break;
case 9:
SUPP_REG_WR(9, data); break;
case 10:
SUPP_REG_WR(10, data); break;
case 11:
SUPP_REG_WR(11, data); break;
case 12:
SUPP_REG_WR(12, data); break;
case 13:
SUPP_REG_WR(13, data); break;
case 14:
SUPP_REG_WR(14, data); break;
default:
ret = -1;
break;
}
/* Restore SRS. */
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
/* Just for show. */
NOP();
NOP();
NOP();
return ret;
}
static long get_debugreg(long pid, unsigned int regno)
{
register int old_srs;
register long data;
if (pid != bp_owner) {
return 0;
}
/* Remember old SRS. */
SPEC_REG_RD(SPEC_REG_SRS, old_srs);
/* Switch to BP bank. */
SUPP_BANK_SEL(BANK_BP);
switch (regno - PT_BP) {
case 0:
SUPP_REG_RD(0, data); break;
case 1:
case 2:
/* error return value? */
data = 0;
break;
case 3:
SUPP_REG_RD(3, data); break;
case 4:
SUPP_REG_RD(4, data); break;
case 5:
SUPP_REG_RD(5, data); break;
case 6:
SUPP_REG_RD(6, data); break;
case 7:
SUPP_REG_RD(7, data); break;
case 8:
SUPP_REG_RD(8, data); break;
case 9:
SUPP_REG_RD(9, data); break;
case 10:
SUPP_REG_RD(10, data); break;
case 11:
SUPP_REG_RD(11, data); break;
case 12:
SUPP_REG_RD(12, data); break;
case 13:
SUPP_REG_RD(13, data); break;
case 14:
SUPP_REG_RD(14, data); break;
default:
/* error return value? */
data = 0;
}
/* Restore SRS. */
SPEC_REG_WR(SPEC_REG_SRS, old_srs);
/* Just for show. */
NOP();
NOP();
NOP();
return data;
}

View file

@ -0,0 +1,173 @@
/*
* Display CPU info in /proc/cpuinfo.
*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/param.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#ifdef CONFIG_PROC_FS
#define HAS_FPU 0x0001
#define HAS_MMU 0x0002
#define HAS_ETHERNET100 0x0004
#define HAS_TOKENRING 0x0008
#define HAS_SCSI 0x0010
#define HAS_ATA 0x0020
#define HAS_USB 0x0040
#define HAS_IRQ_BUG 0x0080
#define HAS_MMU_BUG 0x0100
struct cpu_info {
char *cpu_model;
unsigned short rev;
unsigned short cache_size;
unsigned short flags;
};
/* Some of these model are here for historical reasons only. */
static struct cpu_info cpinfo[] = {
{"ETRAX 1", 0, 0, 0},
{"ETRAX 2", 1, 0, 0},
{"ETRAX 3", 2, 0, 0},
{"ETRAX 4", 3, 0, 0},
{"Simulator", 7, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
{"ETRAX 100", 8, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG},
{"ETRAX 100", 9, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA},
{"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
| HAS_MMU | HAS_MMU_BUG},
{"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
| HAS_MMU},
#ifdef CONFIG_ETRAXFS
{"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
#else
{"ARTPEC-3", 32, 32, HAS_ETHERNET100 | HAS_MMU},
#endif
{"Unknown", 0, 0, 0}
};
int show_cpuinfo(struct seq_file *m, void *v)
{
int i;
int cpu = (int)v - 1;
unsigned long revision;
struct cpu_info *info;
info = &cpinfo[ARRAY_SIZE(cpinfo) - 1];
#ifdef CONFIG_SMP
if (!cpu_online(cpu))
return 0;
#endif
revision = rdvr();
for (i = 0; i < ARRAY_SIZE(cpinfo); i++) {
if (cpinfo[i].rev == revision) {
info = &cpinfo[i];
break;
}
}
return seq_printf(m,
"processor\t: %d\n"
"cpu\t\t: CRIS\n"
"cpu revision\t: %lu\n"
"cpu model\t: %s\n"
"cache size\t: %d KB\n"
"fpu\t\t: %s\n"
"mmu\t\t: %s\n"
"mmu DMA bug\t: %s\n"
"ethernet\t: %s Mbps\n"
"token ring\t: %s\n"
"scsi\t\t: %s\n"
"ata\t\t: %s\n"
"usb\t\t: %s\n"
"bogomips\t: %lu.%02lu\n\n",
cpu,
revision,
info->cpu_model,
info->cache_size,
info->flags & HAS_FPU ? "yes" : "no",
info->flags & HAS_MMU ? "yes" : "no",
info->flags & HAS_MMU_BUG ? "yes" : "no",
info->flags & HAS_ETHERNET100 ? "10/100" : "10",
info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no",
info->flags & HAS_SCSI ? "yes" : "no",
info->flags & HAS_ATA ? "yes" : "no",
info->flags & HAS_USB ? "yes" : "no",
(loops_per_jiffy * HZ + 500) / 500000,
((loops_per_jiffy * HZ + 500) / 5000) % 100);
}
#endif /* CONFIG_PROC_FS */
void show_etrax_copyright(void)
{
#ifdef CONFIG_ETRAXFS
printk(KERN_INFO "Linux/CRISv32 port on ETRAX FS "
"(C) 2003, 2004 Axis Communications AB\n");
#else
printk(KERN_INFO "Linux/CRISv32 port on ARTPEC-3 "
"(C) 2003-2009 Axis Communications AB\n");
#endif
}
static struct i2c_board_info __initdata i2c_info[] = {
{I2C_BOARD_INFO("camblock", 0x43)},
{I2C_BOARD_INFO("tmp100", 0x48)},
{I2C_BOARD_INFO("tmp100", 0x4A)},
{I2C_BOARD_INFO("tmp100", 0x4C)},
{I2C_BOARD_INFO("tmp100", 0x4D)},
{I2C_BOARD_INFO("tmp100", 0x4E)},
#ifdef CONFIG_RTC_DRV_PCF8563
{I2C_BOARD_INFO("pcf8563", 0x51)},
#endif
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
{I2C_BOARD_INFO("vgpio", 0x20)},
{I2C_BOARD_INFO("vgpio", 0x21)},
#endif
{I2C_BOARD_INFO("pca9536", 0x41)},
{I2C_BOARD_INFO("fnp300", 0x40)},
{I2C_BOARD_INFO("fnp300", 0x42)},
{I2C_BOARD_INFO("adc101", 0x54)},
};
static struct i2c_board_info __initdata i2c_info2[] = {
{I2C_BOARD_INFO("camblock", 0x43)},
{I2C_BOARD_INFO("tmp100", 0x48)},
{I2C_BOARD_INFO("tmp100", 0x4A)},
{I2C_BOARD_INFO("tmp100", 0x4C)},
{I2C_BOARD_INFO("tmp100", 0x4D)},
{I2C_BOARD_INFO("tmp100", 0x4E)},
#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
{I2C_BOARD_INFO("vgpio", 0x20)},
{I2C_BOARD_INFO("vgpio", 0x21)},
#endif
{I2C_BOARD_INFO("pca9536", 0x41)},
{I2C_BOARD_INFO("fnp300", 0x40)},
{I2C_BOARD_INFO("fnp300", 0x42)},
{I2C_BOARD_INFO("adc101", 0x54)},
};
static struct i2c_board_info __initdata i2c_info3[] = {
{I2C_BOARD_INFO("adc101", 0x54)},
};
static int __init etrax_init(void)
{
i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
i2c_register_board_info(1, i2c_info2, ARRAY_SIZE(i2c_info2));
i2c_register_board_info(2, i2c_info3, ARRAY_SIZE(i2c_info3));
return 0;
}
arch_initcall(etrax_init);

View file

@ -0,0 +1,537 @@
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <arch/ptrace.h>
#include <arch/hwregs/cpu_vect.h>
extern unsigned long cris_signal_return_page;
/*
* A syscall in CRIS is really a "break 13" instruction, which is 2
* bytes. The registers is manipulated so upon return the instruction
* will be executed again.
*
* This relies on that PC points to the instruction after the break call.
*/
#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->erp -= 2;
/* Signal frames. */
struct signal_frame {
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS - 1];
unsigned char retcode[8]; /* Trampoline code. */
};
struct rt_signal_frame {
struct siginfo *pinfo;
void *puc;
struct siginfo info;
struct ucontext uc;
unsigned char retcode[8]; /* Trampoline code. */
};
void do_signal(int restart, struct pt_regs *regs);
void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
struct pt_regs *regs);
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
unsigned int err = 0;
unsigned long old_usp;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
/*
* Restore the registers from &sc->regs. sc is already checked
* for VERIFY_READ since the signal_frame was previously
* checked in sys_sigreturn().
*/
if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
goto badframe;
/* Make that the user-mode flag is set. */
regs->ccs |= (1 << (U_CCS_BITNR + CCS_SHIFT));
/* Restore the old USP. */
err |= __get_user(old_usp, &sc->usp);
wrusp(old_usp);
return err;
badframe:
return 1;
}
asmlinkage int sys_sigreturn(void)
{
struct pt_regs *regs = current_pt_regs();
sigset_t set;
struct signal_frame __user *frame;
unsigned long oldspc = regs->spc;
unsigned long oldccs = regs->ccs;
frame = (struct signal_frame *) rdusp();
/*
* Since the signal is stacked on a dword boundary, the frame
* should be dword aligned here as well. It it's not, then the
* user is trying some funny business.
*/
if (((long)frame) & 3)
goto badframe;
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__get_user(set.sig[0], &frame->sc.oldmask) ||
(_NSIG_WORDS > 1 && __copy_from_user(&set.sig[1],
frame->extramask,
sizeof(frame->extramask))))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
keep_debug_flags(oldccs, oldspc, regs);
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
asmlinkage int sys_rt_sigreturn(void)
{
struct pt_regs *regs = current_pt_regs();
sigset_t set;
struct rt_signal_frame __user *frame;
unsigned long oldspc = regs->spc;
unsigned long oldccs = regs->ccs;
frame = (struct rt_signal_frame *) rdusp();
/*
* Since the signal is stacked on a dword boundary, the frame
* should be dword aligned here as well. It it's not, then the
* user is trying some funny business.
*/
if (((long)frame) & 3)
goto badframe;
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
keep_debug_flags(oldccs, oldspc, regs);
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/* Setup a signal frame. */
static int
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
unsigned long mask)
{
int err;
unsigned long usp;
err = 0;
usp = rdusp();
/*
* Copy the registers. They are located first in sc, so it's
* possible to use sc directly.
*/
err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
err |= __put_user(mask, &sc->oldmask);
err |= __put_user(usp, &sc->usp);
return err;
}
/* Figure out where to put the new signal frame - usually on the stack. */
static inline void __user *
get_sigframe(struct ksignal *ksig, size_t frame_size)
{
unsigned long sp = sigsp(rdusp(), ksig);
/* Make sure the frame is dword-aligned. */
sp &= ~3;
return (void __user *)(sp - frame_size);
}
/* Grab and setup a signal frame.
*
* Basically a lot of state-info is stacked, and arranged for the
* user-mode program to return to the kernel using either a trampiline
* which performs the syscall sigreturn(), or a provided user-mode
* trampoline.
*/
static int
setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
int err;
unsigned long return_ip;
struct signal_frame __user *frame;
err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
if (err)
return -EFAULT;
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err)
return -EFAULT;
/*
* Set up to return from user-space. If provided, use a stub
* already located in user-space.
*/
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ksig->ka.sa.sa_restorer;
} else {
/* Trampoline - the desired return ip is in the signal return page. */
return_ip = cris_signal_return_page;
/*
* This is movu.w __NR_sigreturn, r9; break 13;
*
* WE DO NOT USE IT ANY MORE! It's only left here for historical
* reasons and because gdb uses it as a signature to notice
* signal handler stack frames.
*/
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
err |= __put_user(__NR_sigreturn, (short __user*)(frame->retcode+2));
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
}
if (err)
return -EFAULT;
/*
* Set up registers for signal handler.
*
* Where the code enters now.
* Where the code enter later.
* First argument, signo.
*/
regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
regs->srp = return_ip;
regs->r10 = ksig->sig;
/* Actually move the USP to reflect the stacked frame. */
wrusp((unsigned long)frame);
return 0;
}
static int
setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
int err;
unsigned long return_ip;
struct rt_signal_frame __user *frame;
err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
/* TODO: what is the current->exec_domain stuff and invmap ? */
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
if (err)
return -EFAULT;
/* Clear all the bits of the ucontext we don't use. */
err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
err |= __save_altstack(&frame->uc.uc_stack, rdusp());
if (err)
return -EFAULT;
/*
* Set up to return from user-space. If provided, use a stub
* already located in user-space.
*/
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long) ksig->ka.sa.sa_restorer;
} else {
/* Trampoline - the desired return ip is in the signal return page. */
return_ip = cris_signal_return_page + 6;
/*
* This is movu.w __NR_rt_sigreturn, r9; break 13;
*
* WE DO NOT USE IT ANY MORE! It's only left here for historical
* reasons and because gdb uses it as a signature to notice
* signal handler stack frames.
*/
err |= __put_user(0x9c5f, (short __user*)(frame->retcode+0));
err |= __put_user(__NR_rt_sigreturn,
(short __user*)(frame->retcode+2));
err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
}
if (err)
return -EFAULT;
/*
* Set up registers for signal handler.
*
* Where the code enters now.
* Where the code enters later.
* First argument is signo.
* Second argument is (siginfo_t *).
* Third argument is unused.
*/
regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
regs->srp = return_ip;
regs->r10 = ksig->sig;
regs->r11 = (unsigned long) &frame->info;
regs->r12 = 0;
/* Actually move the usp to reflect the stacked frame. */
wrusp((unsigned long)frame);
return 0;
}
/* Invoke a signal handler to, well, handle the signal. */
static inline void
handle_signal(int canrestart, struct ksignal *ksig, struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
int ret;
/* Check if this got called from a system call. */
if (canrestart) {
/* If so, check system call restarting. */
switch (regs->r10) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
/*
* This means that the syscall should
* only be restarted if there was no
* handler for the signal, and since
* this point isn't reached unless
* there is a handler, there's no need
* to restart.
*/
regs->r10 = -EINTR;
break;
case -ERESTARTSYS:
/*
* This means restart the syscall if
* there is no handler, or the handler
* was registered with SA_RESTART.
*/
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
regs->r10 = -EINTR;
break;
}
/* Fall through. */
case -ERESTARTNOINTR:
/*
* This means that the syscall should
* be called again after the signal
* handler returns.
*/
RESTART_CRIS_SYS(regs);
break;
}
}
/* Set up the stack frame. */
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(ksig, oldset, regs);
else
ret = setup_frame(ksig, oldset, regs);
signal_setup_done(ret, ksig, 0);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Also note that the regs structure given here as an argument, is the latest
* pushed pt_regs. It may or may not be the same as the first pushed registers
* when the initial usermode->kernelmode transition took place. Therefore
* we can use user_mode(regs) to see if we came directly from kernel or user
* mode below.
*/
void
do_signal(int canrestart, struct pt_regs *regs)
{
struct ksignal ksig;
/*
* The common case should go fast, which is why this point is
* reached from kernel-mode. If that's the case, just return
* without doing anything.
*/
if (!user_mode(regs))
return;
if (get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
handle_signal(canrestart, &ksig, regs);
return;
}
/* Got here from a system call? */
if (canrestart) {
/* Restart the system call - no handlers present. */
if (regs->r10 == -ERESTARTNOHAND ||
regs->r10 == -ERESTARTSYS ||
regs->r10 == -ERESTARTNOINTR) {
RESTART_CRIS_SYS(regs);
}
if (regs->r10 == -ERESTART_RESTARTBLOCK){
regs->r9 = __NR_restart_syscall;
regs->erp -= 2;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
restore_saved_sigmask();
}
asmlinkage void
ugdb_trap_user(struct thread_info *ti, int sig)
{
if (((user_regs(ti)->exs & 0xff00) >> 8) != SINGLE_STEP_INTR_VECT) {
/* Zero single-step PC if the reason we stopped wasn't a single
step exception. This is to avoid relying on it when it isn't
reliable. */
user_regs(ti)->spc = 0;
}
/* FIXME: Filter out false h/w breakpoint hits (i.e. EDA
not within any configured h/w breakpoint range). Synchronize with
what already exists for kernel debugging. */
if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) {
/* Break 8: subtract 2 from ERP unless in a delay slot. */
if (!(user_regs(ti)->erp & 0x1))
user_regs(ti)->erp -= 2;
}
sys_kill(ti->task->pid, sig);
}
void
keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
struct pt_regs *regs)
{
if (oldccs & (1 << Q_CCS_BITNR)) {
/* Pending single step due to single-stepping the break 13
in the signal trampoline: keep the Q flag. */
regs->ccs |= (1 << Q_CCS_BITNR);
/* S flag should be set - complain if it's not. */
if (!(oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT)))) {
printk("Q flag but no S flag?");
}
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
/* Assume the SPC is valid and interesting. */
regs->spc = oldspc;
} else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) {
/* If a h/w bp was set in the signal handler we need
to keep the S flag. */
regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
/* Don't keep the old SPC though; if we got here due to
a single-step, the Q flag should have been set. */
} else if (regs->spc) {
/* If we were single-stepping *before* the signal was taken,
we don't want to restore that state now, because GDB will
have forgotten all about it. */
regs->spc = 0;
regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
}
}
/* Set up the trampolines on the signal return page. */
int __init
cris_init_signal(void)
{
u16* data = kmalloc(PAGE_SIZE, GFP_KERNEL);
/* This is movu.w __NR_sigreturn, r9; break 13; */
data[0] = 0x9c5f;
data[1] = __NR_sigreturn;
data[2] = 0xe93d;
/* This is movu.w __NR_rt_sigreturn, r9; break 13; */
data[3] = 0x9c5f;
data[4] = __NR_rt_sigreturn;
data[5] = 0xe93d;
/* Map to userspace with appropriate permissions (no write access...) */
cris_signal_return_page = (unsigned long)
__ioremap_prot(virt_to_phys(data), PAGE_SIZE, PAGE_SIGNAL_TRAMPOLINE);
return 0;
}
__initcall(cris_init_signal);

View file

@ -0,0 +1,358 @@
#include <linux/types.h>
#include <asm/delay.h>
#include <irq.h>
#include <hwregs/intr_vect.h>
#include <hwregs/intr_vect_defs.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
#include <hwregs/asm/mmu_defs_asm.h>
#include <hwregs/supp_reg.h>
#include <linux/atomic.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#define IPI_SCHEDULE 1
#define IPI_CALL 2
#define IPI_FLUSH_TLB 4
#define IPI_BOOT 8
#define FLUSH_ALL (void*)0xffffffff
/* Vector of locks used for various atomic operations */
spinlock_t cris_atomic_locks[] = {
[0 ... LOCK_COUNT - 1] = __SPIN_LOCK_UNLOCKED(cris_atomic_locks)
};
/* CPU masks */
cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
EXPORT_SYMBOL(phys_cpu_present_map);
/* Variables used during SMP boot */
volatile int cpu_now_booting = 0;
volatile struct thread_info *smp_init_current_idle_thread;
/* Variables used during IPI */
static DEFINE_SPINLOCK(call_lock);
static DEFINE_SPINLOCK(tlbstate_lock);
struct call_data_struct {
void (*func) (void *info);
void *info;
int wait;
};
static struct call_data_struct * call_data;
static struct mm_struct* flush_mm;
static struct vm_area_struct* flush_vma;
static unsigned long flush_addr;
/* Mode registers */
static unsigned long irq_regs[NR_CPUS] = {
regi_irq,
regi_irq2
};
static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id);
static int send_ipi(int vector, int wait, cpumask_t cpu_mask);
static struct irqaction irq_ipi = {
.handler = crisv32_ipi_interrupt,
.flags = 0,
.name = "ipi",
};
extern void cris_mmu_init(void);
extern void cris_timer_init(void);
/* SMP initialization */
void __init smp_prepare_cpus(unsigned int max_cpus)
{
int i;
/* From now on we can expect IPIs so set them up */
setup_irq(IPI_INTR_VECT, &irq_ipi);
/* Mark all possible CPUs as present */
for (i = 0; i < max_cpus; i++)
cpumask_set_cpu(i, &phys_cpu_present_map);
}
void smp_prepare_boot_cpu(void)
{
/* PGD pointer has moved after per_cpu initialization so
* update the MMU.
*/
pgd_t **pgd;
pgd = (pgd_t**)&per_cpu(current_pgd, smp_processor_id());
SUPP_BANK_SEL(1);
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
SUPP_BANK_SEL(2);
SUPP_REG_WR(RW_MM_TLB_PGD, pgd);
set_cpu_online(0, true);
cpumask_set_cpu(0, &phys_cpu_present_map);
set_cpu_possible(0, true);
}
void __init smp_cpus_done(unsigned int max_cpus)
{
}
/* Bring one cpu online.*/
static int __init
smp_boot_one_cpu(int cpuid, struct task_struct idle)
{
unsigned timeout;
cpumask_t cpu_mask;
cpumask_clear(&cpu_mask);
task_thread_info(idle)->cpu = cpuid;
/* Information to the CPU that is about to boot */
smp_init_current_idle_thread = task_thread_info(idle);
cpu_now_booting = cpuid;
/* Kick it */
set_cpu_online(cpuid, true);
cpumask_set_cpu(cpuid, &cpu_mask);
send_ipi(IPI_BOOT, 0, cpu_mask);
set_cpu_online(cpuid, false);
/* Wait for CPU to come online */
for (timeout = 0; timeout < 10000; timeout++) {
if(cpu_online(cpuid)) {
cpu_now_booting = 0;
smp_init_current_idle_thread = NULL;
return 0; /* CPU online */
}
udelay(100);
barrier();
}
printk(KERN_CRIT "SMP: CPU:%d is stuck.\n", cpuid);
return -1;
}
/* Secondary CPUs starts using C here. Here we need to setup CPU
* specific stuff such as the local timer and the MMU. */
void __init smp_callin(void)
{
int cpu = cpu_now_booting;
reg_intr_vect_rw_mask vect_mask = {0};
/* Initialise the idle task for this CPU */
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
/* Set up MMU */
cris_mmu_init();
__flush_tlb_all();
/* Setup local timer. */
cris_timer_init();
/* Enable IRQ and idle */
REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask);
crisv32_unmask_irq(IPI_INTR_VECT);
crisv32_unmask_irq(TIMER0_INTR_VECT);
preempt_disable();
notify_cpu_starting(cpu);
local_irq_enable();
set_cpu_online(cpu, true);
cpu_startup_entry(CPUHP_ONLINE);
}
/* Stop execution on this CPU.*/
void stop_this_cpu(void* dummy)
{
local_irq_disable();
asm volatile("halt");
}
/* Other calls */
void smp_send_stop(void)
{
smp_call_function(stop_this_cpu, NULL, 0);
}
int setup_profiling_timer(unsigned int multiplier)
{
return -EINVAL;
}
/* cache_decay_ticks is used by the scheduler to decide if a process
* is "hot" on one CPU. A higher value means a higher penalty to move
* a process to another CPU. Our cache is rather small so we report
* 1 tick.
*/
unsigned long cache_decay_ticks = 1;
int __cpu_up(unsigned int cpu, struct task_struct *tidle)
{
smp_boot_one_cpu(cpu, tidle);
return cpu_online(cpu) ? 0 : -ENOSYS;
}
void smp_send_reschedule(int cpu)
{
cpumask_t cpu_mask;
cpumask_clear(&cpu_mask);
cpumask_set_cpu(cpu, &cpu_mask);
send_ipi(IPI_SCHEDULE, 0, cpu_mask);
}
/* TLB flushing
*
* Flush needs to be done on the local CPU and on any other CPU that
* may have the same mapping. The mm->cpu_vm_mask is used to keep track
* of which CPUs that a specific process has been executed on.
*/
void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr)
{
unsigned long flags;
cpumask_t cpu_mask;
spin_lock_irqsave(&tlbstate_lock, flags);
cpu_mask = (mm == FLUSH_ALL ? cpu_all_mask : *mm_cpumask(mm));
cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
flush_mm = mm;
flush_vma = vma;
flush_addr = addr;
send_ipi(IPI_FLUSH_TLB, 1, cpu_mask);
spin_unlock_irqrestore(&tlbstate_lock, flags);
}
void flush_tlb_all(void)
{
__flush_tlb_all();
flush_tlb_common(FLUSH_ALL, FLUSH_ALL, 0);
}
void flush_tlb_mm(struct mm_struct *mm)
{
__flush_tlb_mm(mm);
flush_tlb_common(mm, FLUSH_ALL, 0);
/* No more mappings in other CPUs */
cpumask_clear(mm_cpumask(mm));
cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
}
void flush_tlb_page(struct vm_area_struct *vma,
unsigned long addr)
{
__flush_tlb_page(vma, addr);
flush_tlb_common(vma->vm_mm, vma, addr);
}
/* Inter processor interrupts
*
* The IPIs are used for:
* * Force a schedule on a CPU
* * FLush TLB on other CPUs
* * Call a function on other CPUs
*/
int send_ipi(int vector, int wait, cpumask_t cpu_mask)
{
int i = 0;
reg_intr_vect_rw_ipi ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
int ret = 0;
/* Calculate CPUs to send to. */
cpumask_and(&cpu_mask, &cpu_mask, cpu_online_mask);
/* Send the IPI. */
for_each_cpu(i, &cpu_mask)
{
ipi.vector |= vector;
REG_WR(intr_vect, irq_regs[i], rw_ipi, ipi);
}
/* Wait for IPI to finish on other CPUS */
if (wait) {
for_each_cpu(i, &cpu_mask) {
int j;
for (j = 0 ; j < 1000; j++) {
ipi = REG_RD(intr_vect, irq_regs[i], rw_ipi);
if (!ipi.vector)
break;
udelay(100);
}
/* Timeout? */
if (ipi.vector) {
printk("SMP call timeout from %d to %d\n", smp_processor_id(), i);
ret = -ETIMEDOUT;
dump_stack();
}
}
}
return ret;
}
/*
* You must not call this function with disabled interrupts or from a
* hardware interrupt handler or from a bottom half handler.
*/
int smp_call_function(void (*func)(void *info), void *info, int wait)
{
cpumask_t cpu_mask;
struct call_data_struct data;
int ret;
cpumask_setall(&cpu_mask);
cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
WARN_ON(irqs_disabled());
data.func = func;
data.info = info;
data.wait = wait;
spin_lock(&call_lock);
call_data = &data;
ret = send_ipi(IPI_CALL, wait, cpu_mask);
spin_unlock(&call_lock);
return ret;
}
irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id)
{
void (*func) (void *info) = call_data->func;
void *info = call_data->info;
reg_intr_vect_rw_ipi ipi;
ipi = REG_RD(intr_vect, irq_regs[smp_processor_id()], rw_ipi);
if (ipi.vector & IPI_SCHEDULE) {
scheduler_ipi();
}
if (ipi.vector & IPI_CALL) {
func(info);
}
if (ipi.vector & IPI_FLUSH_TLB) {
if (flush_mm == FLUSH_ALL)
__flush_tlb_all();
else if (flush_vma == FLUSH_ALL)
__flush_tlb_mm(flush_mm);
else
__flush_tlb_page(flush_vma, flush_addr);
}
ipi.vector = 0;
REG_WR(intr_vect, irq_regs[smp_processor_id()], rw_ipi, ipi);
return IRQ_HANDLED;
}

View file

@ -0,0 +1,317 @@
/*
* linux/arch/cris/arch-v32/kernel/time.c
*
* Copyright (C) 2003-2010 Axis Communications AB
*
*/
#include <linux/timex.h>
#include <linux/time.h>
#include <linux/clocksource.h>
#include <linux/interrupt.h>
#include <linux/swap.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/cpufreq.h>
#include <asm/types.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/timer_defs.h>
#include <hwregs/intr_vect_defs.h>
#ifdef CONFIG_CRIS_MACH_ARTPEC3
#include <hwregs/clkgen_defs.h>
#endif
/* Watchdog defines */
#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */
#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */
/* Number of 763 counts before watchdog bites */
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1)
/* Register the continuos readonly timer available in FS and ARTPEC-3. */
static cycle_t read_cont_rotime(struct clocksource *cs)
{
return (u32)REG_RD(timer, regi_timer0, r_time);
}
static struct clocksource cont_rotime = {
.name = "crisv32_rotime",
.rating = 300,
.read = read_cont_rotime,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static int __init etrax_init_cont_rotime(void)
{
clocksource_register_khz(&cont_rotime, 100000);
return 0;
}
arch_initcall(etrax_init_cont_rotime);
unsigned long timer_regs[NR_CPUS] =
{
regi_timer0,
#ifdef CONFIG_SMP
regi_timer2
#endif
};
extern int set_rtc_mmss(unsigned long nowtime);
#ifdef CONFIG_CPU_FREQ
static int
cris_time_freq_notifier(struct notifier_block *nb, unsigned long val,
void *data);
static struct notifier_block cris_time_freq_notifier_block = {
.notifier_call = cris_time_freq_notifier,
};
#endif
unsigned long get_ns_in_jiffie(void)
{
reg_timer_r_tmr0_data data;
unsigned long ns;
data = REG_RD(timer, regi_timer0, r_tmr0_data);
ns = (TIMER0_DIV - data) * 10;
return ns;
}
/* From timer MDS describing the hardware watchdog:
* 4.3.1 Watchdog Operation
* The watchdog timer is an 8-bit timer with a configurable start value.
* Once started the watchdog counts downwards with a frequency of 763 Hz
* (100/131072 MHz). When the watchdog counts down to 1, it generates an
* NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the
* chip.
*/
/* This gives us 1.3 ms to do something useful when the NMI comes */
/* Right now, starting the watchdog is the same as resetting it */
#define start_watchdog reset_watchdog
#if defined(CONFIG_ETRAX_WATCHDOG)
static short int watchdog_key = 42; /* arbitrary 7 bit number */
#endif
/* Number of pages to consider "out of memory". It is normal that the memory
* is used though, so set this really low. */
#define WATCHDOG_MIN_FREE_PAGES 8
void reset_watchdog(void)
{
#if defined(CONFIG_ETRAX_WATCHDOG)
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
/* Only keep watchdog happy as long as we have memory left! */
if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
/* Reset the watchdog with the inverse of the old key */
/* Invert key, which is 7 bits */
watchdog_key ^= ETRAX_WD_KEY_MASK;
wd_ctrl.cnt = ETRAX_WD_CNT;
wd_ctrl.cmd = regk_timer_start;
wd_ctrl.key = watchdog_key;
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
}
#endif
}
/* stop the watchdog - we still need the correct key */
void stop_watchdog(void)
{
#if defined(CONFIG_ETRAX_WATCHDOG)
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */
wd_ctrl.cnt = ETRAX_WD_CNT;
wd_ctrl.cmd = regk_timer_stop;
wd_ctrl.key = watchdog_key;
REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl);
#endif
}
extern void show_registers(struct pt_regs *regs);
void handle_watchdog_bite(struct pt_regs *regs)
{
#if defined(CONFIG_ETRAX_WATCHDOG)
extern int cause_of_death;
oops_in_progress = 1;
printk(KERN_WARNING "Watchdog bite\n");
/* Check if forced restart or unexpected watchdog */
if (cause_of_death == 0xbedead) {
#ifdef CONFIG_CRIS_MACH_ARTPEC3
/* There is a bug in Artpec-3 (voodoo TR 78) that requires
* us to go to lower frequency for the reset to be reliable
*/
reg_clkgen_rw_clk_ctrl ctrl =
REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
ctrl.pll = 0;
REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl);
#endif
while(1);
}
/* Unexpected watchdog, stop the watchdog and dump registers. */
stop_watchdog();
printk(KERN_WARNING "Oops: bitten by watchdog\n");
show_registers(regs);
oops_in_progress = 0;
#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
reset_watchdog();
#endif
while(1) /* nothing */;
#endif
}
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "xtime_update()" routine every clocktick.
*/
extern void cris_do_profile(struct pt_regs *regs);
static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = get_irq_regs();
int cpu = smp_processor_id();
reg_timer_r_masked_intr masked_intr;
reg_timer_rw_ack_intr ack_intr = { 0 };
/* Check if the timer interrupt is for us (a tmr0 int) */
masked_intr = REG_RD(timer, timer_regs[cpu], r_masked_intr);
if (!masked_intr.tmr0)
return IRQ_NONE;
/* Acknowledge the timer irq. */
ack_intr.tmr0 = 1;
REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr);
/* Reset watchdog otherwise it resets us! */
reset_watchdog();
/* Update statistics. */
update_process_times(user_mode(regs));
cris_do_profile(regs); /* Save profiling information */
/* The master CPU is responsible for the time keeping. */
if (cpu != 0)
return IRQ_HANDLED;
/* Call the real timer interrupt handler */
xtime_update(1);
return IRQ_HANDLED;
}
/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */
static struct irqaction irq_timer = {
.handler = timer_interrupt,
.flags = IRQF_SHARED,
.name = "timer"
};
void __init cris_timer_init(void)
{
int cpu = smp_processor_id();
reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 };
reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV;
reg_timer_rw_intr_mask timer_intr_mask;
/* Setup the etrax timers.
* Base frequency is 100MHz, divider 1000000 -> 100 HZ
* We use timer0, so timer1 is free.
* The trig timer is used by the fasttimer API if enabled.
*/
tmr0_ctrl.op = regk_timer_ld;
tmr0_ctrl.freq = regk_timer_f100;
REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div);
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */
tmr0_ctrl.op = regk_timer_run;
REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */
/* Enable the timer irq. */
timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask);
timer_intr_mask.tmr0 = 1;
REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask);
}
void __init time_init(void)
{
reg_intr_vect_rw_mask intr_mask;
/* 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;
/* Start CPU local timer. */
cris_timer_init();
/* Enable the timer irq in global config. */
intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1);
intr_mask.timer0 = 1;
REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask);
/* Now actually register the timer irq handler that calls
* timer_interrupt(). */
setup_irq(TIMER0_INTR_VECT, &irq_timer);
/* Enable watchdog if we should use one. */
#if defined(CONFIG_ETRAX_WATCHDOG)
printk(KERN_INFO "Enabling watchdog...\n");
start_watchdog();
/* If we use the hardware watchdog, we want to trap it as an NMI
* and dump registers before it resets us. For this to happen, we
* must set the "m" NMI enable flag (which once set, is unset only
* when an NMI is taken). */
{
unsigned long flags;
local_save_flags(flags);
flags |= (1<<30); /* NMI M flag is at bit 30 */
local_irq_restore(flags);
}
#endif
#ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&cris_time_freq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
#endif
}
#ifdef CONFIG_CPU_FREQ
static int
cris_time_freq_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freqs = data;
if (val == CPUFREQ_POSTCHANGE) {
reg_timer_r_tmr0_data data;
reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ;
do {
data = REG_RD(timer, timer_regs[freqs->cpu],
r_tmr0_data);
} while (data > 20);
REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div);
}
return 0;
}
#endif

View file

@ -0,0 +1,193 @@
/*
* Copyright (C) 2003-2006, Axis Communications AB.
*/
#include <linux/ptrace.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <hwregs/supp_reg.h>
#include <hwregs/intr_vect_defs.h>
#include <asm/irq.h>
void show_registers(struct pt_regs *regs)
{
/*
* It's possible to use either the USP register or current->thread.usp.
* USP might not correspond to the current process for all cases this
* function is called, and current->thread.usp isn't up to date for the
* current process. Experience shows that using USP is the way to go.
*/
unsigned long usp = rdusp();
unsigned long d_mmu_cause;
unsigned long i_mmu_cause;
printk("CPU: %d\n", smp_processor_id());
printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n",
regs->erp, regs->srp, regs->ccs, usp, regs->mof);
printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n",
regs->r0, regs->r1, regs->r2, regs->r3);
printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
regs->r4, regs->r5, regs->r6, regs->r7);
printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
regs->r8, regs->r9, regs->r10, regs->r11);
printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
regs->r12, regs->r13, regs->orig_r10, regs->acr);
printk(" sp: %08lx\n", (unsigned long)regs);
SUPP_BANK_SEL(BANK_IM);
SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);
SUPP_BANK_SEL(BANK_DM);
SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);
printk(" Data MMU Cause: %08lx\n", d_mmu_cause);
printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, (unsigned long)current);
/*
* When in-kernel, we also print out the stack and code at the
* time of the fault..
*/
if (!user_mode(regs)) {
int i;
show_stack(NULL, (unsigned long *)usp);
/*
* If the previous stack-dump wasn't a kernel one, dump the
* kernel stack now.
*/
if (usp != 0)
show_stack(NULL, NULL);
printk("\nCode: ");
if (regs->erp < PAGE_OFFSET)
goto bad_value;
/*
* Quite often the value at regs->erp doesn't point to the
* interesting instruction, which often is the previous
* instruction. So dump at an offset large enough that the
* instruction decoding should be in sync at the interesting
* point, but small enough to fit on a row. The regs->erp
* location is pointed out in a ksymoops-friendly way by
* wrapping the byte for that address in parenthesises.
*/
for (i = -12; i < 12; i++) {
unsigned char c;
if (__get_user(c, &((unsigned char *)regs->erp)[i])) {
bad_value:
printk(" Bad IP value.");
break;
}
if (i == 0)
printk("(%02x) ", c);
else
printk("%02x ", c);
}
printk("\n");
}
}
void arch_enable_nmi(void)
{
unsigned long flags;
local_save_flags(flags);
flags |= (1 << 30); /* NMI M flag is at bit 30 */
local_irq_restore(flags);
}
extern void (*nmi_handler)(struct pt_regs *);
void handle_nmi(struct pt_regs *regs)
{
#ifdef CONFIG_ETRAXFS
reg_intr_vect_r_nmi r;
#endif
if (nmi_handler)
nmi_handler(regs);
#ifdef CONFIG_ETRAXFS
/* Wait until nmi is no longer active. */
do {
r = REG_RD(intr_vect, regi_irq, r_nmi);
} while (r.ext == regk_intr_vect_on);
#endif
}
#ifdef CONFIG_BUG
extern void die_if_kernel(const char *str, struct pt_regs *regs, long err);
/* Copy of the regs at BUG() time. */
struct pt_regs BUG_regs;
void do_BUG(char *file, unsigned int line)
{
printk("kernel BUG at %s:%d!\n", file, line);
die_if_kernel("Oops", &BUG_regs, 0);
}
EXPORT_SYMBOL(do_BUG);
void fixup_BUG(struct pt_regs *regs)
{
BUG_regs = *regs;
#ifdef CONFIG_DEBUG_BUGVERBOSE
/*
* Fixup the BUG arguments through exception handlers.
*/
{
const struct exception_table_entry *fixup;
/*
* ERP points at the "break 14" + 2, compensate for the 2
* bytes.
*/
fixup = search_exception_tables(instruction_pointer(regs) - 2);
if (fixup) {
/* Adjust the instruction pointer in the stackframe. */
instruction_pointer(regs) = fixup->fixup;
arch_fixup(regs);
}
}
#else
/* Dont try to lookup the filename + line, just dump regs. */
do_BUG("unknown", 0);
#endif
}
/*
* Break 14 handler. Save regs and jump into the fixup_BUG.
*/
__asm__ ( ".text\n\t"
".global breakh_BUG\n\t"
"breakh_BUG:\n\t"
SAVE_ALL
KGDB_FIXUP
"move.d $sp, $r10\n\t"
"jsr fixup_BUG\n\t"
"nop\n\t"
"jump ret_from_intr\n\t"
"nop\n\t");
#ifdef CONFIG_DEBUG_BUGVERBOSE
void
handle_BUG(struct pt_regs *regs)
{
}
#endif
#endif