/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 1994 - 2000, 2001 by Ralf Baechle
 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
 * Copyright (C) 2001 MIPS Technologies, Inc.
 */
#include <linux/config.h>
#include <linux/init.h>
#include <linux/sys.h>

#include <asm/addrspace.h>
#include <asm/asm.h>
#include <asm/cacheops.h>
#include <asm/current.h>
#include <asm/errno.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/pgtable-bits.h>
#include <asm/stackframe.h>
#include <asm/processor.h>
#include <asm/regdef.h>
#include <asm/fpregdef.h>
#include <asm/unistd.h>
#include <asm/isadep.h>


		.text
		.align	5
		.set	push
		.set	reorder
FEXPORT(ret_from_irq)
FEXPORT(ret_from_exception)
		lw	t0, PT_STATUS(sp)	# returning to kernel mode?
		andi	t0, t0, KU_USER
		beqz	t0, restore_all

FEXPORT(ret_from_sys_call)		# here to prevent code duplication
ret_from_schedule:
		mfc0	t0, CP0_STATUS	# need_resched and signals atomic test
		ori	t0, t0, 1
		xori	t0, t0, 1
		mtc0	t0, CP0_STATUS
		SSNOP; SSNOP; SSNOP

		lw	v0, TASK_NEED_RESCHED($28)
		lw	v1, TASK_SIGPENDING($28)
		bnez	v0, reschedule
		bnez	v1, signal_return
restore_all:	.set	noat
#if defined(CONFIG_RTHAL)
		lw	t0, (rthal + 8)	# sti
		beqz	t0, 1f
		nop
		lw	t0, (rthal + 4)	# cli
		jal	t0
		nop
1:
#endif
		RESTORE_ALL_AND_RET
		.set	at

/* Put this behind restore_all for the sake of the branch prediction.  */
signal_return:
		.type	signal_return, @function

		mfc0	t0, CP0_STATUS
		ori	t0, t0, 1
		mtc0	t0, CP0_STATUS

		move	a0, zero
		move	a1, sp
		jal	do_signal
		b	restore_all

reschedule:
		jal	schedule
		b	ret_from_schedule

/*
 * Common spurious interrupt handler.
 */
		.text
		.align  5
LEAF(spurious_interrupt)
		/*
		 * Someone tried to fool us by sending an interrupt but we
		 * couldn't find a cause for it.
		 */
		lui     t1,%hi(irq_err_count)
		lw      t0,%lo(irq_err_count)(t1)
		addiu   t0,1
		sw      t0,%lo(irq_err_count)(t1)
		j	ret_from_irq
END(spurious_interrupt)

		__INIT

		.set	reorder

NESTED(except_vec1_generic, 0, sp)
		PANIC("Exception vector 1 called")
END(except_vec1_generic)

		/*
		 * General exception vector.  Used for all CPUs except R4000
		 * and R4400 SC and MC versions.
		 */
NESTED(except_vec3_generic, 0, sp)
		mfc0	k1, CP0_CAUSE
		la	k0, exception_handlers
		andi	k1, k1, 0x7c
		addu	k0, k0, k1
		lw	k0, (k0)
		jr	k0
END(except_vec3_generic)
		.set	at

		/* General exception vector R4000 version. */
NESTED(except_vec3_r4000, 0, sp)
		.set	push
		.set    mips3
		.set	noat
#if defined(R5432_CP0_INTERRUPT_WAR)
		mfc0	k0, CP0_INDEX
#endif
		mfc0	k1, CP0_CAUSE
		li	k0, 31<<2
		andi	k1, k1, 0x7c
		.set	noreorder
		beq	k1, k0, handle_vced
		 li	k0, 14<<2
		beq	k1, k0, handle_vcei
		 la	k0, exception_handlers
		.set	reorder
		addu	k0, k0, k1
		lw	k0, (k0)
		jr	k0

		/*
		 * Big shit, we now may have two dirty primary cache lines for
		 * the same physical address.  We can savely invalidate the
		 * line pointed to by c0_badvaddr because after return from
		 * this exception handler the load / store will be re-executed.
		 */
handle_vced:
		mfc0	k0, CP0_BADVADDR
		li	k1, -4
		and	k0, k1
		mtc0	zero, CP0_TAGLO
		cache	Index_Store_Tag_D,(k0)
		cache	Hit_Writeback_Inv_SD,(k0)
#ifdef CONFIG_PROC_FS
		lui	k0, %hi(vced_count)
		lw	k1, %lo(vced_count)(k0)
		addiu	k1, 1
		sw	k1, %lo(vced_count)(k0)
#endif
		eret

handle_vcei:
		mfc0	k0, CP0_BADVADDR
		cache	Hit_Writeback_Inv_SD, (k0)	# also cleans pi
#ifdef CONFIG_PROC_FS
		lui	k0, %hi(vcei_count)
		lw	k1, %lo(vcei_count)(k0)
		addiu	k1, 1
		sw	k1, %lo(vcei_count)(k0)
#endif
		eret
		.set    pop
END(except_vec3_r4000)

		__FINIT

#if defined(CONFIG_RTHAL)

#define RT_RA	0
#define RT_R0	4
#define RT_R1	8
#define RT_R2	12
#define RT_R3	16
#define RT_R4	20
#define RT_R5	24
#define RT_R6	28
#define RT_R7	32
#define RT_R8	36
#define RT_R9	40
#define RT_R10	44
#define RT_R11	48
#define RT_R12	52
#define RT_R13	56
#define RT_R14	60
#define RT_R15	64
#define RT_R16	68
#define RT_R17	72
#define RT_R18	76
#define RT_R19	80
#define RT_R20	84
#define RT_R21	88
#define RT_R22	92
#define RT_R23	96
#define RT_R24	100
#define RT_R25	104
#define RT_R26	108
#define RT_R27	112
#define RT_R28	116
#define RT_R29	120
#define RT_R30	124
#define RT_R31	128
#define RT_SIZE 132

		.set	push
		.set	noreorder
		.set	noat
		.set	nomacro

NESTED(rthal_sw_thread, RT_SIZE, sp)
		.set	push

		subu	sp, RT_SIZE
		sw	$1, RT_R1(sp)
		sw	$2, RT_R2(sp)
		sw	$3, RT_R3(sp)
		sw	$4, RT_R4(sp)
		sw	$5, RT_R5(sp)
		sw	$6, RT_R6(sp)
		sw	$7, RT_R7(sp)
		sw	$8, RT_R8(sp)
		sw	$9, RT_R9(sp)
		sw	$10, RT_R10(sp)
		sw	$11, RT_R11(sp)
		sw	$12, RT_R12(sp)
		sw	$13, RT_R13(sp)
		sw	$14, RT_R14(sp)
		sw	$15, RT_R15(sp)
		sw	$16, RT_R16(sp)
		sw	$17, RT_R17(sp)
		sw	$18, RT_R18(sp)
		sw	$19, RT_R19(sp)
		sw	$20, RT_R20(sp)
		sw	$21, RT_R21(sp)
		sw	$22, RT_R22(sp)
		sw	$23, RT_R23(sp)
		sw	$24, RT_R24(sp)
		sw	$25, RT_R25(sp)
		sw	$26, RT_R26(sp)
		sw	$27, RT_R27(sp)
		sw	$28, RT_R28(sp)
		sw	$29, RT_R29(sp)
		sw	$30, RT_R30(sp)
		sw	$31, RT_R31(sp)

		lw	$8,  ($4)
		sw	$29, ($8)
		sw	$5,  ($4)
		lw	$29, ($5)

		.set	pop
		lw	$1, RT_R1(sp)
		lw	$2, RT_R2(sp)
		lw	$3, RT_R3(sp)
		lw	$4, RT_R4(sp)
		lw	$5, RT_R5(sp)
		lw	$6, RT_R6(sp)
		lw	$7, RT_R7(sp)
		lw	$8, RT_R8(sp)
		lw	$9, RT_R9(sp)
		lw	$10, RT_R10(sp)
		lw	$11, RT_R11(sp)
		lw	$12, RT_R12(sp)
		lw	$13, RT_R13(sp)
		lw	$14, RT_R14(sp)
		lw	$15, RT_R15(sp)
		lw	$16, RT_R16(sp)
		lw	$17, RT_R17(sp)
		lw	$18, RT_R18(sp)
		lw	$19, RT_R19(sp)
		lw	$20, RT_R20(sp)
		lw	$21, RT_R21(sp)
		lw	$22, RT_R22(sp)
		lw	$23, RT_R23(sp)
		lw	$24, RT_R24(sp)
		lw	$25, RT_R25(sp)
		lw	$26, RT_R26(sp)
		lw	$27, RT_R27(sp)
		lw	$28, RT_R28(sp)
		lw	$30, RT_R30(sp)
		lw	$31, RT_R31(sp)
		addu	sp, RT_SIZE

		j	$31
		nop

		.set	at
		.set	reorder
		.set	macro

END(rthal_sw_thread)

#endif /* CONFIG_RTHAL */

/*
 * Build a default exception handler for the exceptions that don't need
 * special handlers.  If you didn't know yet - I *like* playing games with
 * the C preprocessor ...
 */
#define __BUILD_clear_none(exception)
#define __BUILD_clear_sti(exception)                                    \
		STI
#define __BUILD_clear_cli(exception)                                    \
		CLI
#define __BUILD_clear_fpe(exception)                                    \
		cfc1	a1,fcr31;                                       \
		li	a2,~(0x3f<<12);                                 \
		and	a2,a1;                                          \
		ctc1	a2,fcr31;                                       \
		STI
#define __BUILD_clear_ade(exception)                                    \
		.set	reorder;					\
		MFC0	t0,CP0_BADVADDR;                                \
		.set	noreorder;					\
		REG_S	t0,PT_BVADDR(sp);                               \
		KMODE
#define __BUILD_silent(exception)

#define fmt "Got %s at %08lx.\n"

#define __BUILD_verbose(exception)                                      \
		la	a1,8f;                                          \
		TEXT	(#exception);                                   \
		REG_L	a2,PT_EPC(sp);                                  \
		PRINT(fmt)
#define __BUILD_count(exception)                                        \
		.set	reorder;                                        \
		lw	t0,exception_count_##exception;                 \
		.set	noreorder;                                      \
		addiu	t0, 1;                                          \
		sw	t0,exception_count_##exception;                 \
		.data;                                                  \
EXPORT(exception_count_##exception);                                    \
		.word	0;                                              \
		.previous;
#define BUILD_HANDLER(exception,handler,clear,verbose)                  \
		.align	5;                                              \
NESTED(handle_##exception, PT_SIZE, sp);		                \
		.set	noat;                                           \
		SAVE_ALL;                                               \
FEXPORT(handle_##exception##_int);					\
		__BUILD_clear_##clear(exception);                       \
		.set	at;                                             \
		__BUILD_##verbose(exception);                           \
		jal	do_##handler;                                   \
		 move	a0, sp;                                         \
		j	ret_from_exception;                             \
		 nop;                                                   \
END(handle_##exception)

		BUILD_HANDLER(adel,ade,ade,silent)		/* #4  */
		BUILD_HANDLER(ades,ade,ade,silent)		/* #5  */
		BUILD_HANDLER(ibe,be,cli,silent)		/* #6  */
		BUILD_HANDLER(dbe,be,cli,silent)		/* #7  */
		BUILD_HANDLER(bp,bp,sti,silent)			/* #9  */
		BUILD_HANDLER(ri,ri,sti,silent)			/* #10 */
		BUILD_HANDLER(cpu,cpu,sti,silent)		/* #11 */
		BUILD_HANDLER(ov,ov,sti,silent)			/* #12 */
		BUILD_HANDLER(tr,tr,sti,silent)			/* #13 */
		BUILD_HANDLER(fpe,fpe,fpe,silent)		/* #15 */
		BUILD_HANDLER(mdmx,mdmx,sti,silent)		/* #22 */
		BUILD_HANDLER(watch,watch,sti,silent)		/* #23 */
		BUILD_HANDLER(mcheck,mcheck,cli,silent)		/* #24 */
		BUILD_HANDLER(reserved,reserved,sti,silent)	/* others */

		.set	pop

/*
 * Table of syscalls
 */
		.data
		.align	PTRLOG
EXPORT(sys_call_table)
#define SYS(call, narg) PTR call

		/* Reserved space for all SVR4 syscalls. */
		.space	(1000)*PTRSIZE

#ifdef CONFIG_BINFMT_IRIX
		/* 32bit IRIX5 system calls. */
#include "irix5sys.h"
#else
		.space	(1000)*PTRSIZE		/* No IRIX syscalls */
#endif

		/* Reserved space for all the BSD43 and POSIX syscalls. */
		.space	(2000)*PTRSIZE

		/* Linux flavoured syscalls. */
#include "syscalls.h"

/*
 * Number of arguments of each syscall
 */
EXPORT(sys_narg_table)
#undef SYS
#define SYS(call, narg) .byte narg

		/* Reserved space for all SVR4 flavoured syscalls. */
		.space	(1000)

#ifdef CONFIG_BINFMT_IRIX
		/* 32bit IRIX5 system calls. */
#include "irix5sys.h"
#else
		.space	(1000)			/* No IRIX syscalls */
#endif

		/* Reserved space for all the BSD43 and POSIX syscalls. */
		.space	(2000)

		/* Linux flavoured syscalls. */
#include "syscalls.h"
