/* 
 * Mach Operating System
 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 * HISTORY
 * $Log:	mips_cpu.s,v $
 * Revision 2.15  93/01/14  17:51:49  danner
 * 	Improper register save in fpa_intr() was clobbering user
 * 	register s1 [branch_delay() was storing over it].
 * 	[92/11/30            af]
 * 
 * Revision 2.14  92/02/19  15:09:25  elf
 * 	Changed #-style comments, for ANSI cpp.
 * 	[92/02/19  13:11:19  rpd]
 * 
 * Revision 2.13  91/08/24  12:23:08  af
 * 	Renames of old spl functions, still used as "default" spls.
 * 	[91/08/02  03:05:32  af]
 * 
 * Revision 2.12  91/05/18  14:35:56  rpd
 * 	Put a nop between splhigh and kdbsplhigh.
 * 	[91/03/26            rpd]
 * 
 * Revision 2.11  91/05/14  17:35:46  mrt
 * 	Correcting copyright
 * 
 * Revision 2.10  91/05/13  06:06:13  af
 * 	splsoftclock() and splnet() were swapped, oops.
 * 	Removed misleading unused spl aliases.
 * 	[91/05/12  15:58:50  af]
 * 
 * Revision 2.8.1.2  91/03/30  13:36:41  af
 * 	splsoftclock() and splnet() were swapped, oops.
 * 
 * Revision 2.8.1.1  91/02/21  18:50:09  af
 * 	Removed misleading unused spl aliases.
 * 	[91/02/21            af]
 * 
 * Revision 2.9  91/03/16  14:56:34  rpd
 * 	Pulled mips_float_state out of mips_machine_state.
 * 	[91/02/18            rpd]
 * 
 * Revision 2.8  91/02/14  14:35:52  mrt
 * 	Fixed a number of problems in the FPA emulation code.
 * 	Added delay() and its autotime function, this way we
 * 	can distinguish between DS3100 and DS2100.
 * 	[91/02/12  12:37:13  af]
 * 
 * Revision 2.7  91/02/05  17:49:33  mrt
 * 	Added author notices
 * 	[91/02/04  11:23:34  mrt]
 * 
 * 	Changed to use new Mach copyright
 * 	[91/02/02  12:27:21  mrt]
 * 
 * Revision 2.6  91/01/08  15:50:30  rpd
 * 	Updated for new symbolic field names.
 * 	[91/01/03  22:10:08  rpd]
 * 
 * 	Replaced thread_doexception with exception.
 * 	[90/12/27  21:22:32  rpd]
 * 
 * 	Added kdbsplhigh and kdbsplx.
 * 	[90/11/26            rpd]
 * 
 * 	Changed disable_fpa for new pcb organization.
 * 	[90/11/12            rpd]
 * 
 * Revision 2.5  90/09/09  14:33:28  rpd
 * 	Fixed interrupt loop generated by some invalid FP instructions.
 * 	[90/08/30  17:00:33  af]
 * 
 * Revision 2.4  90/08/07  22:29:38  rpd
 * 	Documented 3max spl setup.
 * 	[90/08/07  15:23:27  af]
 * 
 * Revision 2.3.1.1  90/05/30  15:38:35  af
 * 	Documented 3max spl setup.
 * 
 * Revision 2.3  90/01/22  23:07:23  af
 * 	Modified calling sequence for fpa_intr, to work with MIPSCo code
 * 	as well as with mine.  Moved check_memory() elsewhere.
 * 	[90/01/20  16:48:55  af]
 * 
 * Revision 2.2  89/11/29  14:14:36  af
 * 	Added fpa_intr(), and related functions for FP emulation.
 * 	[89/11/26  10:36:09  af]
 * 
 * 	Use special recovery mechanism while sizing memory, as
 * 	the standard one might endup calling vm_fault.
 * 	[89/11/16  14:46:48  af]
 * 
 * 	Moved memory checking code here from locore.
 * 	[89/11/03  16:29:52  af]
 * 
 * 	Created.
 * 	[89/10/04            af]
 */
/*
 *	File: mips_cpu.s
 * 	Author: Alessandro Forin, Carnegie Mellon University
 *	Date:	10/89
 *
 *	CPU related miscellaneous operations
 *
 *	Includes common operations on the status register (spl)
 *	and other CPU registers, and the FPA registers.
 */

#include <mach/mips/asm.h>
#include <mips/mips_cpu.h>
#include <mach/mips/vm_param.h>
#include <mach/mips/mips_instruction.h>
#include <mips/thread.h>
#include <mach/exception.h>

#include <assym.s>

	.set	noreorder

/*
 *	Object:
 *		cpu_get_prid			EXPORTED function
 *
 *		Read the CPU's PRocessor IDentifier register
 *
 *	Arguments:
 *		none
 */
LEAF(cpu_get_prid)
	mfc0	v0,c0_prid
	j	ra
	nop
	END(cpu_get_prid)


/*
 *	Object:
 *		cop0_busy			EXPORTED function
 *		wbflush				EXPORTED function alias
 *
 *		Wait if coprocessor 0 is busy
 *
 *	Arguments:
 *		none
 *
 *	The coprocessor 0 flag is used in the MIPS chipset to
 *	connect the write buffer to the CPU.  This indicates
 *	whether writes are pending or not, and it is necessary
 *	in I/O operations for better timings and to avoid
 *	write clashes to the same address (hence the MIPS name).
 *	I suspect most of the invocations to this function could
 *	be avoided by proper use of the "volatile" attribute.
 */
LEAF(cop0_busy)
XLEAF(wbflush)
	nop			/* it's 3 or 4 deep, waste 4 cycles first */
	nop
	nop
	nop
1:
	bc0f	1b
	nop
	j	ra
	nop
	END(cop0_busy)


/*
 *	Object:
 *		fpa_owner			EXPORTED thread_t
 *
 * 	Current owner of the FPA, if any
 *
 */
	BSS(fpa_owner, 4)

/*
 *	Object:
 *		check_fpa			EXPORTED function
 *		disable_fpa			EXPORTED function
 *
 *		Release fpa ownership
 *
 *	Arguments:
 *		th				thread_t
 *		exiting				boolean_t
 *
 *	'th' is the FPA owner, true or presumed.
 *	'exiting' is true if thread is being terminated.
 *
 *	disable_fpa forbids use of the fpa to a given thread,
 *	so that its state will be reloaded when/if necessary
 *	from the pcb.
 *
 *	NOTE: fpa_intr() knows that this function preserves a2.
 *
 */
LEAF(check_fpa)
	.set	reorder
	lw	v0,fpa_owner
	bne	a0,v0,disable_fpa	/* not owner, but make sure. */
	bne	a1,zero,1f		/* exiting, don't save state */
	lw	a3,fpa_type
	beq	a3,zero,1f		/* no fpa ? */
	lw	a3,THREAD_PCB(a0)
	lw	a3,MSS_SIZE+MMS_MFS(a3)	/* get fp save area */
	.set	noreorder
	mfc0	v1,c0_status		/* enable fpa for the kernel */
	li	v0,SR_CU1		
	mtc0	v0,c0_status		/* no interrupts */
	nop
	nop
	cfc1	v0,fpa_csr
	nop
	sw	v0,MFS_CSR(a3)
	cfc1	v0,fpa_eir
	nop
	sw	v0,MFS_EIR(a3)
	swc1	$f31, MFS_REGS+31*4(a3)
	swc1	$f30, MFS_REGS+30*4(a3)
	swc1	$f29, MFS_REGS+29*4(a3)
	swc1	$f28, MFS_REGS+28*4(a3)
	swc1	$f27, MFS_REGS+27*4(a3)
	swc1	$f26, MFS_REGS+26*4(a3)
	swc1	$f25, MFS_REGS+25*4(a3)
	swc1	$f24, MFS_REGS+24*4(a3)
	swc1	$f23, MFS_REGS+23*4(a3)
	swc1	$f22, MFS_REGS+22*4(a3)
	swc1	$f21, MFS_REGS+21*4(a3)
	swc1	$f20, MFS_REGS+20*4(a3)
	swc1	$f19, MFS_REGS+19*4(a3)
	swc1	$f18, MFS_REGS+18*4(a3)
	swc1	$f17, MFS_REGS+17*4(a3)
	swc1	$f16, MFS_REGS+16*4(a3)
	swc1	$f15, MFS_REGS+15*4(a3)
	swc1	$f14, MFS_REGS+14*4(a3)
	swc1	$f13, MFS_REGS+13*4(a3)
	swc1	$f12, MFS_REGS+12*4(a3)
	swc1	$f11, MFS_REGS+11*4(a3)
	swc1	$f10, MFS_REGS+10*4(a3)
	swc1	$f9,  MFS_REGS+ 9*4(a3)
	swc1	$f8,  MFS_REGS+ 8*4(a3)
	swc1	$f7,  MFS_REGS+ 7*4(a3)
	swc1	$f6,  MFS_REGS+ 6*4(a3)
	swc1	$f5,  MFS_REGS+ 5*4(a3)
	swc1	$f4,  MFS_REGS+ 4*4(a3)
	swc1	$f3,  MFS_REGS+ 3*4(a3)
	swc1	$f2,  MFS_REGS+ 2*4(a3)
	swc1	$f1,  MFS_REGS+ 1*4(a3)
	swc1	$f0,  MFS_REGS+ 0*4(a3)
	ctc1	zero,fpa_csr		/* clear any pending interrupts */
	mtc0	v1,c0_status		/* disable fpa for kernel,restore intr */
	nop
	.set	reorder

1:	sw	zero,fpa_owner		/* fpa free now */
					/* fall through.. */

XLEAF(disable_fpa)
	lw	a0,THREAD_PCB(a0)
	lw	a1,MSS_SR(a0)		/* user's sr */
	and	a1,~SR_CU1		/* make fpa unusable */
	sw	a1,MSS_SR(a0)

2:	j	ra
	.set	noreorder
	END(check_fpa)

/*
 *	Object:
 *		fpa_intr			EXPORTED function
 *
 *		Handle FPA interrupts
 *
 *	Arguments:
 *		ss_ptr				mips_saved_state *
 *		status				unsigned
 */
#define FPA_INTR_FRAME (4+4)*4
NESTED(fpa_intr, FPA_INTR_FRAME, zero)
	subu	sp,FPA_INTR_FRAME
	sw	ra,(FPA_INTR_FRAME-4)(sp)

	li	t0,SR_CU1		/* enable FPA for kernel, no intr */
	mtc0	t0,c0_status
	sw	s0,(FPA_INTR_FRAME-8)(sp)
	sw	s1,(FPA_INTR_FRAME-12)(sp)
	sw	s2,(FPA_INTR_FRAME-16)(sp)
	cfc1	s1,fpa_csr		/* get csr */
	move	s0,a0

	lw	t1,MSS_CAUSE(s0)	/* check for bdelay */

	ctc1	zero,fpa_csr		/* clear FP interrupt */

	lw	s2,MSS_PC(s0)		/* get pc (and keep it) */
	bgez	t1,1f			/* no bdelay, just.. */
	lw	a1,0(s2)		/* get inst */

	jal	branch_delay		/* yes bdelay, find where to branch to.. */
	nop

	lw	a1,4(s2)		/* get inst from bdelay */
	subu	s2,v0,4			/* compensate for later incr */
	move	a0,s0			/* ss_ptr */
1:	li	t0,op_cop1
	srl	t1,a1,26
	bne	t1,t0,genexc		/* not an FP instruction ??? */
	li	a2,EXC_MIPS_FLT_UNIMP	

	jal	emulate_fpa
	move	a2,s1

	bne	v0,zero,genexc		/* exception ? */
	move	a2,v0			/* exc code, if any */

	addu	s2,4			/* all went well, proceed */

	mtc0	zero,c0_status		/* disable fpa&intr */

	b	10f
	sw	s2,MSS_PC(s0)

genexc:
	lw	s0,the_current_thread
	ctc1	s1,fpa_csr		/* put true fpa_csr back and */
	move	a0,s0
	jal	check_fpa		/* save FPA status in PCB */
	move	a1,zero
	/*
	 * We need to something here to clear the fpa of
	 * the effects of an illegal instruction.  Otherwise
	 * we might loop in fpa interrupts after we do the spl0
	 */
	ctc1	zero,fpa_csr

	/*
	 * NOTE: This knows too much, but yes we can only get here
	 *	 from user mode: kernel does not use FPA
	 */
	jal	simple_spl0		/* lower priority, disable fpa */
	nop

	li	a0,EXC_ARITHMETIC
	move	a1,a2
	jal	exception
	move	a2,zero
	/*NOTREACHED*/

10:	lw	ra,(FPA_INTR_FRAME-4)(sp)
	lw	s2,(FPA_INTR_FRAME-16)(sp)
	lw	s1,(FPA_INTR_FRAME-12)(sp)
	lw	s0,(FPA_INTR_FRAME-8)(sp)
	j	ra
	addu	sp,FPA_INTR_FRAME

	END(fpa_intr)

/*
 *	Object:
 *		fpa_get_reg			EXPORTED function
 *		fpa_put_reg			EXPORTED function
 *
 *		Peek/poke into the FPA
 *
 *	Arguments:
 *		reg				unsigned
 *		val				unsigned
 *
 *	FPA must be enabled, or else.
 */
LEAF(fpa_get_reg)
	la	v0,1f
	sll	a0,3
	addu	a0,v0
	j	a0
	nop
1:	b	2f
	mfc1	v0,$f0
	b	2f
	mfc1	v0,$f1
	b	2f
	mfc1	v0,$f2
	b	2f
	mfc1	v0,$f3
	b	2f
	mfc1	v0,$f4
	b	2f
	mfc1	v0,$f5
	b	2f
	mfc1	v0,$f6
	b	2f
	mfc1	v0,$f7
	b	2f
	mfc1	v0,$f8
	b	2f
	mfc1	v0,$f9
	b	2f
	mfc1	v0,$f10
	b	2f
	mfc1	v0,$f11
	b	2f
	mfc1	v0,$f12
	b	2f
	mfc1	v0,$f13
	b	2f
	mfc1	v0,$f14
	b	2f
	mfc1	v0,$f15
	b	2f
	mfc1	v0,$f16
	b	2f
	mfc1	v0,$f17
	b	2f
	mfc1	v0,$f18
	b	2f
	mfc1	v0,$f19
	b	2f
	mfc1	v0,$f20
	b	2f
	mfc1	v0,$f21
	b	2f
	mfc1	v0,$f22
	b	2f
	mfc1	v0,$f23
	b	2f
	mfc1	v0,$f24
	b	2f
	mfc1	v0,$f25
	b	2f
	mfc1	v0,$f26
	b	2f
	mfc1	v0,$f27
	b	2f
	mfc1	v0,$f28
	b	2f
	mfc1	v0,$f29
	b	2f
	mfc1	v0,$f30
	mfc1	v0,$f31
2:	j	ra
	nop
	END(fpa_get_reg)

LEAF(fpa_put_reg)
	la	v0,1f
	sll	a0,3
	addu	a0,v0
	j	a0
	nop
1:	j	ra
	mtc1	a1,$f0
	j	ra
	mtc1	a1,$f1
	j	ra
	mtc1	a1,$f2
	j	ra
	mtc1	a1,$f3
	j	ra
	mtc1	a1,$f4
	j	ra
	mtc1	a1,$f5
	j	ra
	mtc1	a1,$f6
	j	ra
	mtc1	a1,$f7
	j	ra
	mtc1	a1,$f8
	j	ra
	mtc1	a1,$f9
	j	ra
	mtc1	a1,$f10
	j	ra
	mtc1	a1,$f11
	j	ra
	mtc1	a1,$f12
	j	ra
	mtc1	a1,$f13
	j	ra
	mtc1	a1,$f14
	j	ra
	mtc1	a1,$f15
	j	ra
	mtc1	a1,$f16
	j	ra
	mtc1	a1,$f17
	j	ra
	mtc1	a1,$f18
	j	ra
	mtc1	a1,$f19
	j	ra
	mtc1	a1,$f20
	j	ra
	mtc1	a1,$f21
	j	ra
	mtc1	a1,$f22
	j	ra
	mtc1	a1,$f23
	j	ra
	mtc1	a1,$f24
	j	ra
	mtc1	a1,$f25
	j	ra
	mtc1	a1,$f26
	j	ra
	mtc1	a1,$f27
	j	ra
	mtc1	a1,$f28
	j	ra
	mtc1	a1,$f29
	j	ra
	mtc1	a1,$f30
	j	ra
	mtc1	a1,$f31
	END(fpa_put_reg)

/*
 *	Object:
 *		fpa_get_irr			EXPORTED function
 *
 *		Read the FPA's Implementation Revision Register
 *
 *	Arguments:
 *		none
 */
LEAF(fpa_get_irr)
	mfc0	t0,c0_status		/* save sr */
	li	v0,SR_CU1		/* enable fpa, no interrupts */
	mtc0	v0,c0_status
	nop
	nop
	cfc1	v0,fpa_irr		/* get register */
	nop
	j	ra
	mtc0	t0,c0_status		/* restore sr */
	END(fpa_get_irr)

/*
 *	Object:
 *		fpa_set_irr			EXPORTED function
 *
 *		Writes to the FPA's IRR register
 *
 *	Arguments:
 *		a0				unsigned
 *
 *	Might have strange meanings on some boxes.
 */
LEAF(fpa_set_irr)
	mfc0	v0,c0_status
	li	t0,SR_CU1		/* enable fpa, no interrupts */
	mtc0	t0,c0_status
	nop
	nop
	ctc1	a0,fpa_irr
	j	ra
	mtc0	v0,c0_status
	END(fpa_set_irr)


/*
 *	Object:
 *		fpa_set_csr			EXPORTED function
 *
 *		Set the FPA status register
 *
 *	Arguments:
 *		a0					unsigned
 *
 *	a0 is copied unconditionally into the FPA csr
 */
LEAF(fpa_set_csr)
	mfc0	v0,c0_status
	li	t0,SR_CU1		/* enable fpa, no interrupts */
	mtc0	t0,c0_status
	nop
	nop
	ctc1	a0,fpa_csr
	nop
	mtc0	v0,c0_status
	j	ra
	nop
	END(fpa_set_csr)


/*
 *	Object:
 *		Priorities			LOCAL definitions
 *
 *	Interrupts at IPL 0 and 1 are software generated.
 *	Devices interrupt at the following IPL on
 *	the currently supported boxes:
 *
 *	MIPSCo M-500:
 *			 7	memory
 *			 6	profiling clock (optional)
 *			 5	fpa
 *			 4	scheduling clock
 *			 3	console
 *			 2	VME devices
 *
 *	DEC Pmax:
 *			 7	fpa
 *			 6	memory/video
 *			 5	scheduling clock
 *			 4	console lines
 *			 3	lance
 *			 2	sii
 *
 *	DEC 3max:
 *			 7	fpa
 *			 6	-
 *			 5	memory
 *			 4	-
 *			 3	scheduling clock
 *			 2	IO devices
 *
 *	DEC 3min:
 *			 7	fpa
 *			 6	halt button
 *			 5	system board devs and memory
 *			 4	TC slot 2
 *			 3	TC slot 1
 *			 2	TC slot 0
 */
#include <mips/mips_box.h>

/*
 *	Object:
 *		simple_splx			EXPORTED function
 *
 *		Restore priority level
 *
 *	Arguments:
 *		a0				unsigned
 *
 *	Set priority level to the value in a0, returns NOTHING
 *	Actually, since the IPL is not a separate register on MIPS just
 *	use the entire content of the status register.
 */
LEAF(simple_splx)
	mtc0	a0,c0_status
	j	ra
	move	v0,zero		/* just in case someone uses it */
	END(simple_splx)

/*
 *	Object:
 *		simple_spl0			EXPORTED function
 *
 *		Enable all interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 *	NOTE: fpa_intr() knows that this function preserves a1.
 */
LEAF(simple_spl0)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV0
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_spl0)

/*
 *	Object:
 *		simple_splsoftclock		EXPORTED function
 *
 *		Block software clock interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_splsoftclock)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV2
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splsoftclock)

/*
 *	Object:
 *		simple_splnet			EXPORTED function
 *
 *		Block software network interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_splnet)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV1
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splnet)

/*
 *	Object:
 *		simple_splimp			EXPORTED function
 *
 *		Block network hardware interrupts
 *
 *	Arguments:
 *		none
 *
 *	Unlike Vax, does not block hardware clock.
 *	Returns the previous content of the status register
 */
LEAF(simple_splimp)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV4
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splimp)

/*
 *	Object:
 *		simple_splbio			EXPORTED function
 *
 *		Block all BlockI/O device interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_splbio)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV3
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splbio)

/*
 *	Object:
 *		simple_spltty			EXPORTED function
 *
 *		Block character I/O device interrupts (console)
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_spltty)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV_CONSOLE
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_spltty)

/*
 *	Object:
 *		simple_splclock			EXPORTED function
 *
 *		Block scheduling clock (hardware) interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_splclock)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV6
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splclock)

/*
 *	Object:
 *		simple_splvm			EXPORTED function
 *
 *		Block interrupts that might cause VM faults
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 */
LEAF(simple_splvm)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV7
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splvm)

/*
 *	Object:
 *		simple_splhigh			EXPORTED function
 *		simple_splsched			EXPORTED function
 *
 *		Block all interrupts
 *
 *	Arguments:
 *		none
 *
 *	splsched could probably be lower, but we play safe.
 *	Returns the previous content of the status register
 */
LEAF(simple_splhigh)
XLEAF(simple_splsched)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV8
	mtc0	t0,c0_status
	j	ra
	nop
	END(simple_splhigh)

	/*
	 *	We need space between these functions
	 *	so that single-stepping through splhigh
	 *	doesn't put a breakpoint in kdbsplhigh.
	 */
	nop

/*
 *	Object:
 *		kdbsplhigh			EXPORTED function
 *
 *		Block all interrupts
 *
 *	Arguments:
 *		none
 *
 *	Returns the previous content of the status register
 *
 *	NOTE:	this can ONLY be paired with kdbsplx, with NO
 *		other spls in between.  Caveat emptor.
 */
LEAF(kdbsplhigh)
	mfc0	v0,c0_status
	li	t0,SR_IEc|INT_LEV8
	mtc0	t0,c0_status
	j	ra
	nop
	END(kdbsplhigh)

/*
 *	Object:
 *		kdbsplx				EXPORTED function
 *
 *		Restore priority level
 *
 *	Arguments:
 *		a0				unsigned
 *
 *	Set priority level to the value in a0, returns the previous value.
 *	Actually, since the IPL is not a separate register on MIPS just
 *	use the entire content of the status register.
 *
 *	NOTE:	this can ONLY be paired with kdbsplhigh, with NO
 *		other spls in between.  Caveat emptor.
 */
LEAF(kdbsplx)
	mfc0	v0,c0_status
	mtc0	a0,c0_status
	j	ra
	nop
	END(kdbsplx)

/*
 *	Object:
 *		patch_simple_spl		EXPORTED function
 *
 *		Patch spl function to use a different IPL
 *
 *	Arguments:
 *		a0				(unsigned (*)())
 *		a1				unsigned
 *
 *	M-series and Pmax have opposite byte-order and compile-time
 *	differences are therefore ok.  Other boxes might be different,
 *	but at CMU we'd like to keep single images if at all possible,
 *	for instance between Pmax and 3max.  Therefore this function
 *	is provided to modify appropriately the IPL for the simple_splxxx()
 *	functions at autoconf time.
 *	The IPL (argument a1) should be in the range 0..8
 *	Flushing the Icache is the caller's responsibility.
 *
 */
LEAF(patch_simple_spl)
	la	v0,spl_level
	sll	a1,2			/* array index */
	addu	v0,a1
	lw	v0,0(v0)
	j	ra
	sw	v0,4(a0)
spl_level:
	li	t0,SR_IEc|INT_LEV0
	li	t0,SR_IEc|INT_LEV1
	li	t0,SR_IEc|INT_LEV2
	li	t0,SR_IEc|INT_LEV3
	li	t0,SR_IEc|INT_LEV4
	li	t0,SR_IEc|INT_LEV5
	li	t0,SR_IEc|INT_LEV6
	li	t0,SR_IEc|INT_LEV7
	li	t0,SR_IEc|INT_LEV8
	break	8				/* sanity */
	nop
	END(patch_simple_spl)


/*
 *	Object:
 *		setsoftclock			EXPORTED function
 *
 *		Schedule a software clock interrupt
 *
 *	Arguments:
 *		none
 *
 *	Software interrupts are generated by writing into the cause
 *	register (Kane, pag 5-4).  In theory we should read it first
 *	to make sure we do not drop an IP_LEV0 interrupt, but in
 *	Mach only one software interrupt is used.
 *	If this ever changes, make sure you disable interrupts
 *	across the read/or/write sequence on the cause register.
 */
LEAF(setsoftclock)
	li	v0, IP_LEV1
	mtc0	v0,c0_cause
	j	ra	
	nop
	END(setsoftclock)

/*
 *	Object:
 *		clearsoftclock			EXPORTED function
 *
 *		Acknowledge (clear) a software clock interrupt
 *
 *	Arguments:
 *		none
 *
 *	See notes in setsoftclock.
 */
LEAF(clearsoftclock)
	mtc0	zero,c0_cause
	j	ra
	nop
	END(clearsoftclock)

/*
 *	Object:
 *		delay			EXPORTED function
 *
 *		Busy loop for a given number of microseconds
 *
 *		cpu_speed		EXPORTED unsigned int
 *
 *		loop multiplier for the above function
 *
 *	Arguments:
 *		usecs			unsigned int
 *
 *	This was once written in C and configured by hand.
 *	Now I want to be able to config it automagically
 *	and I cannot be subject to compiler's vagaries.
 *
 */
	BSS(cpu_speed,4)

LEAF(delay)
	lw	v0,cpu_speed	/*  1 cycle if cache hit */
	nop			/*  1 cycle */
	multu	v0,a0		/* 12 cycles */
	mflo	v0		/*  1 cycle */
	addiu	v0,-1		/*  1 cycle */
loop:	bgtz	v0,loop		/*  1 cycle */
	addiu	v0,-1		/*  1 cycle --> body loop 2 cycles */
	j	ra		/*  1 cycle */
	nop			/*  1 cycle */
				/* -------- */
				/* 18+2N cycles */
	END(delay)

/*
 *	Object:
 *		delay_timing_function	EXPORTED function
 *
 *		Autoconfig function for the above delay()
 *
 *	Arguments:
 *		value			unsigned char
 *		loc			unsigned char *
 *		inner_loop_size		int *
 *
 *	Returns:
 *
 *		Number of cycles until (*loc == value)
 *
 *	Keep looping until the location pointed to by loc
 *	is equal to value (which must be != 0).
 *	Used only by clock drivers while configuring
 *	the delay function.
 *
 */
LEAF(delay_timing_function)
	move	t0,zero
	move	t2,zero
1:
	addiu	t0,1		/* 3 cycles in inner loop */
	bne	t2,a0,1b
	lw	t2,0(a1)

	sll	v0,t0,1		/* loop count times three */
	addu	v0,t0
	li	a0,2		/* 2 cycles in delay() above */
	j	ra
	sw	a0,0(a2)

	END(delay_timing_function)

