/*
 * @(#)spl.h: $Revision: 1.41.98.1 $ $Date: 95/07/20 10:46:13 $
 * $Locker: BERT $
 *
 */

#ifndef	SPL_INCLUDED
#define	SPL_INCLUDED

/*
 * This files defines the processor (spl()) levels. It also assigns certain
 * eirr bits. For the series 800, the following interrupt priority levels
 * are defined:
 *
 *
 *	SPLPREEMPTOK() - allow all interrupts
 *		Enables LPMC, powerfail and all eirr bits, except
 *		HPMC_INT, which is reserved for crash dump use.
 *
 *	SPLNOPREEMPT() - disable kernel preemption
 *		Same as SPLPREEMPTOK(), except 'trap type' interrupts are
 *		disabled. 'trap type' interrupts cause the kernel to be
 *		preempted by another process when at SPLPREEMPTOK().
 *		SPLNOPREEMPT() and higher spl levels indicate the kernel
 *		cannot be preempted.
 *		Currently there are 2 'trap type' interrupts:
 *			MPSCHED -  In an MP system, one cpu tells another cpu
 *			  	   to check the runq by sending this interupt
 *				   type.
 *			KPREEMPT - When the kernel is executing and a higher
 *				   priority process should run (eg. clock tick
 *				   recomputed priorities or external interrupt
 *				   caused a wakeup()), the kernel sends itself
 *				   a KPREEMPT interrupt to cause a preemption
 *				   when the spl level is SPLPREEMPTOK().
 *
 *	SPL2() - disable software interrupts
 *		Same as SPLNOPREEMPT(), except 'software priority' interrupts
 *		are disabled. Software interrupts are often sent when
 *		the kernel is executing on the ics at a high interrupt priority
 *		and it wants to schedule some processing to be done later after
 *		all high priority interrupts (eg. device IO) are serviced.
 *		The following software interrupts are used:
 *			SOFTNET    - When a networking IO driver receives an
 *				     inbound packet, it removes the packet
 *				     and schedules other networking code to
 *				     process the packet at a lower priority.
 *				     by sending a SOFTNET interrupt.
 *		In addition to software interrupts, SPL2() also disables
 *		interrupts from low priority Type-B DMA IO modules. Currently,
 *	        there is only one low priority IO module (ieee_8024 - map).
 *
 *	SPL5() - disable IO modules
 *		 Same as SPL2(), except all IO module interrupts are disabled.
 *
 *	SPL6() - disable hard clock and kernel powerfail recovery
 *		 Same as SPL5() except hard clock interrupts are disabled.
 *
 *	SPL7() - disable the world
 *		 Same as SPL6(), except powerfail and RDB interrupts and LPMC
 *		 are disabled - PSW_I is off.
 */

/*
 * WARNING: many of these defines are used by assembly. Therefore,
 * 'C' operators ahould not be used (eg. & | >>, (), etc.)
 */


#ifndef _SYS_STDSYMS_INCLUDED
#ifdef _KERNEL_BUILD
#    include "../h/stdsyms.h"
#else  /* ! _KERNEL_BUILD */
#    include <sys/stdsyms.h>
#endif /* _KERNEL_BUILD */
#endif  /*  _SYS_STDSYMS_INCLUDED  */

#ifdef _KERNEL

#define MSB	0x80000000

#define SPL_LOCK_REL_MASK	0x10
#define SPL_LOCK_REL(x)		((x) & SPL_LOCK_REL_MASK)


#define GPROF_INT_BIT           0
#define GPROF_INT_ENABLE                0x80000000
#define CLOCK_INT_BIT           4
#define CLOCK_INT_ENABLE                0x08000000

#define REMOTE_PFAIL_INT_BIT	1
#define REMOTE_PFAIL_INT_ENABLE		0x40000000

#define KERN_PFR_INT_BIT	2
#define KERN_PFR_INT_ENABLE		0x20000000

/* The CLOCK_RESYNC bit is used by the monarch processor in an MP system
 * to bring a non-monarch processor into the synchronization code.
 * The interval timer synchronization code coordinates the monarch and
 * non-monarch and allows the non-monarch to compute a couple of local
 * variables that may be used by it at any time to compute an estimate
 * of the monarch's itimer value.  The CLOCK_INT_BIT used to be used for
 * this purpose, but was abandoned, since a non-monarch could receive
 * that interrupt either from the monarch or from its own interval timer.
 */
#define CLOCK_RESYNC_INT_BIT	7
#define CLOCK_RESYNC_INT_ENABLE		0x01000000

#define SW_TRIGGER_6_INT_BIT                    20
#define SW_TRIGGER_6_INT_ENABLE         0x00000800

#define SW_TRIGGER_5_INT_BIT                    21
#define SW_TRIGGER_5_INT_ENABLE         0x00000400

#define SW_TRIGGER_4_INT_BIT                    22
#define SW_TRIGGER_4_INT_ENABLE         0x00000200

#define SW_TRIGGER_3_INT_BIT                    23
#define SW_TRIGGER_3_INT_ENABLE         0x00000100

#define SW_TRIGGER_2_INT_BIT                    24
#define SW_TRIGGER_2_INT_ENABLE         0x00000080

#define SW_TRIGGER_1_INT_BIT                    27
#define SW_TRIGGER_1_INT_ENABLE         0x00000010

#define SOFTNET_INT_BIT                         25
#define SOFTNET_INT_ENABLE              0x00000040

#define IOFORW_INT_BIT                          26 
#define IOFORW_INT_ENABLE               0x00000020

/*
 *	First trap type interrupt bit.
 */

#define MPSCHED_INT_BIT                         28
#define MPSCHED_INT_ENABLE              0x00000008

#define UNUSED_TRAP_INT_BIT29                   29
#define UNUSED_TRAP_INT_ENABLE29        0x00000004

#define KPREEMPT_INT_BIT                        30
#define KPREEMPT_INT_ENABLE             0x00000002
/*
 * HPMC_INT_BIT should never be enabled, except by crash dump assembly.
 */
#define HPMC_INT_BIT                            31
#define HPMC_INT_ENABLE                 0x00000001

#define _GPROF_SPL6     GPROF_INT_ENABLE
#define _GPROF_SPL5     GPROF_INT_ENABLE | CLOCK_INT_ENABLE
#define _RDB_SPL6       0x06000000
#define _RDB_SPL5       _RDB_SPL6
#define	RDB_INT_ENABLE  _RDB_SPL6

/*
 * The value for SPL7 is arbitrary, since psw-I is off; it is less than
 * five bits so that comib can be used to test it.
 */
#define SPL7            0x00000007
#define SPL6           (0x40000000 | _GPROF_SPL6 | _RDB_SPL6)
#define SPL5           (0xe1000800 | _GPROF_SPL5 | _RDB_SPL5)
#define SPL4            0xfffffc00
#define SPL3            0xfffffe00
#define SPL2            0xffffff00
#define SPL1            0xffffffc0
#define SPL0            SPLNOPREEMPT

#define SPLNOPREEMPT    0xfffffff0
#define SPLPREEMPTOK	0xfffffffe

/*
 * Note that in addition to RDB/RPF being turned off, the gprof interrupt is
 * also disabled.  The assumption is that if the user of these definitions
 * don't want RDB/RPF then gprof is probably not important to them either.
 * This is leftover from pre-I/O Convergence days.
 */
#define SPL6NORDB       0x40000000
#define SPL6NORPF       _RDB_SPL6

#define SPLIMP		SPL5
#define SPLNET		SPL2

#define LPRIV_INT_BIT		32	/* This is a fake interrupt bit used
					 * in take_a_trap() to cause a
					 * lower priv transfer trap.
					 */

#define MPLOCAL_MASK	(MPSCHED_INT_ENABLE	| KPREEMPT_INT_ENABLE	| \
			 SOFTNET_INT_ENABLE     | \
			 KERN_PFR_INT_ENABLE	| RDB_INT_ENABLE	| \
			 CLOCK_INT_ENABLE	| REMOTE_PFAIL_INT_ENABLE)


#define 	SPINLOCK_EIEM   SPL6


/*
 *  mp_ext_interrupt() uses SPL_LOCK_MASK to determine which external
 *  interrupts need to be processed holding the spl_lock.  External
 *  interrupts included in this mask will have the spl_lock acquired
 *  before their associated handler is called.
 */
#define SPL_LOCK_MASK	(SOFTNET_INT_ENABLE     | RDB_INT_ENABLE)

/*
 * BASEPRI is true when the spl level of eiem passed to us equals that
 *         used in the idle loop in swtch().
 */
#define	BASEPRI(eiem)	(eiem == (unsigned) SPLNOPREEMPT)

/*
 * These following macros GRAB_SPL and FREE_SPL are designed to be used in
 * conjuction with each other.  They will acquire the spl_lock if it is not
 * already held.  The corresponding FREE_SPL routine will release the
 * spl_lock iff the GRAB_SPL routine was the routine to acquire the lock.
 * These macros assume that if the SW_TRIGGER_1_INT_BIT is enabled then
 * the level is below SPL1.
 *
 * Do not use these macros without reading and understanding the notes.
 *
 * Usage:
 * 	GRAB_SPL(GOT_SPL_LOCK, OLD_EIEM, MY_PROCESSOR, RTN)
 *		unsigned int 	*GOT_SPL_LOCK;	flag for FREE_SPL
 *		unsigned int 	*OLD_EIEM;	return value for FREE_SPL
 *		struct mpinfo 	*MY_PROCESSOR;	processor ID
 *		char		*RTN;		routine name for debug
 *
 * N.B. *OLD_EIEM is not modified unless the lock is acquired.
 *	It MUST be initialized to 0 before calling GRAB_SPL!!!
 *
 *	*OLD_EIEM is not modified unless the lock is acquired
 *	Its value is undefined unless GRAB_SPL also sets *GOT_SPL_LOCK to 
 *	true.
 * 	
 * Usage:
 *	FREE_SPL(GOT_SPL_LOCK, OLD_EIEM, MY_PROCESSOR, RTN)
 *		unsigned int 	GOT_SPL_LOCK;	flag from GRAB_SPL
 *		unsigned int	OLD_EIEM;	value to set CR_EIEM to
 *		struct mpinfo	*MY_PROCESSOR;	processor ID
 *		char		*RTN;		routine name for debug
 *
 * N.B.	The spl level after FREE_SPL may be greater than SPL2, and yet the
 * 	spl_lock will still be released.
 *
 */

#ifdef __STDC__
#define rtn_concat(s1, s2)	#s1":" #s2 
#else /* ! __STDC__ */
#define rtn_concat(s1, s2)	"s1: s2"
#endif /* ! __STDC__ */

#define	GRAB_SPL(GOT_SPL_LOCK, OLD_EIEM, MY_PROCESSOR, RTN)	\
{								\
    register unsigned int	eiem;				\
								\
    if (MY_PROCESSOR != ((struct mpinfo *)_spl_lock.sl_owner)) {	\
	_MFCTL(CR_EIEM, eiem);					\
	/*							\
	 * Raise our interrupt level >= SPL1.			\
	 * If the sw trigger level 1 interrupt is enabled, we are 	\
	 * below SPL1						\
	 */							\
	if (eiem & SW_TRIGGER_1_INT_ENABLE)			\
	    eiem = SPL1;					\
	/*							\
	 * This will acquire the spl_lock			\
	 */							\
	*(OLD_EIEM) = splx(eiem);				\
	MP_ASSERT(						\
		MY_PROCESSOR == ((struct mpinfo *)_spl_lock.sl_owner), \
		rtn_concat(RTN,did not get spl_lock));		\
	*(GOT_SPL_LOCK) = TRUE;					\
    }								\
}

#define	FREE_SPL(GOT_SPL_LOCK, OLD_EIEM, MY_PROCESSOR, RTN)	\
{								\
    register unsigned int	eiem;				\
								\
    if (GOT_SPL_LOCK) {						\
	MP_ASSERT(						\
		MY_PROCESSOR == ((struct mpinfo *)_spl_lock.sl_owner),	\
		rtn_concat(RTN,do not own spl_lock and we should));	\
	releasespl_lock();					\
	eiem = OLD_EIEM;					\
	_MTCTL(eiem, CR_EIEM);					\
    }								\
}

#endif /* _KERNEL */
#endif /* SPL_INCLUDED */

