/*-
 * Copyright (c) 1989, 1990 William F. Jolitz.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)icu.s	7.2 (Berkeley) 5/21/91
 *
 *	$Id: icu.s,v 1.3.2.1 1993/11/14 18:13:00 rgrimes Exp $
 */

/*
 * AT/386
 * Vector interrupt control section
 */

/*
 * XXX - this file is now misnamed.  All spls are now soft and the only thing
 * related to the hardware icu is that the bit numbering is the same in the
 * soft priority masks as in the hard ones.
 */

#include "sio.h"
#define	HIGHMASK	0xffff
#define	SOFTCLOCKMASK	0x8000

	.data

	.globl	_cpl
_cpl:	.long	0xffff			/* current priority (all off) */

	.globl	_imen
_imen:	.long	0xffff			/* interrupt mask enable (all off) */

/* 	.globl	_highmask	*/
_highmask:	.long	HIGHMASK

	.globl	_ttymask, _biomask, _netmask
_ttymask:	.long	0
_biomask:	.long	0
_netmask:	.long	0

	.globl	_ipending, _astpending
_ipending:	.long	0
_astpending:	.long	0		/* tells us an AST needs to be taken */

	.globl	_netisr
_netisr:	.long	0		/* set with bits for which queue to service */

vec:
	.long	vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
	.long	vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15

#define	GENSPL(name, mask, event) \
	.globl	_spl/**/name ; \
	ALIGN_TEXT ; \
_spl/**/name: ; \
	COUNT_EVENT(_intrcnt_spl, event) ; \
	movl	_cpl,%eax ; \
	movl	%eax,%edx ; \
	orl	mask,%edx ; \
	movl	%edx,_cpl ; \
	SHOW_CPL ; \
	ret

#define	FASTSPL(mask) \
	movl	mask,_cpl ; \
	SHOW_CPL

#define	FASTSPL_VARMASK(varmask) \
	movl	varmask,%eax ; \
	movl	%eax,_cpl ; \
	SHOW_CPL

	.text

	ALIGN_TEXT
unpend_v:
	COUNT_EVENT(_intrcnt_spl, 0)
	bsfl	%eax,%eax		# slow, but not worth optimizing
	btrl	%eax,_ipending
	jnc	unpend_v_next		# some intr cleared the in-memory bit
	SHOW_IPENDING
	movl	Vresume(,%eax,4),%eax
	testl	%eax,%eax
	je	noresume
	jmp	%eax

	ALIGN_TEXT
/*
 * XXX - must be some fastintr, need to register those too.
 */
noresume:
#if NSIO > 0
	call	_softsio1
#endif
unpend_v_next:
	movl	_cpl,%eax
	movl	%eax,%edx
	notl	%eax
	andl	_ipending,%eax
	je	none_to_unpend
	jmp	unpend_v

/*
 * Handle return from interrupt after device handler finishes
 */
	ALIGN_TEXT
doreti:
	COUNT_EVENT(_intrcnt_spl, 1)
	addl	$4,%esp			# discard unit arg
	popl	%eax			# get previous priority
/*
 * Now interrupt frame is a trap frame!
 *
 * XXX - setting up the interrupt frame to be almost a stack frame is mostly
 * a waste of time.
 */
	movl	%eax,_cpl
	SHOW_CPL
	movl	%eax,%edx
	notl	%eax
	andl	_ipending,%eax
	jne	unpend_v
none_to_unpend:
	testl	%edx,%edx		# returning to zero priority?
	jne	1f			# nope, going to non-zero priority
	movl	_netisr,%eax
	testl	%eax,%eax		# check for softint s/traps
	jne	2f			# there are some
	jmp	test_resched		# XXX - schedule jumps better
	COUNT_EVENT(_intrcnt_spl, 2)	# XXX

	ALIGN_TEXT			# XXX
1:					# XXX
	COUNT_EVENT(_intrcnt_spl, 3)
	popl	%es
	popl	%ds
	popal
	addl	$8,%esp
	iret

#include "../net/netisr.h"

#define DONET(s, c, event) ; \
	.globl	c ; \
	btrl	$s,_netisr ; \
	jnc	1f ; \
	COUNT_EVENT(_intrcnt_spl, event) ; \
	call	c ; \
1:

	ALIGN_TEXT
2:
	COUNT_EVENT(_intrcnt_spl, 4)
/*
 * XXX - might need extra locking while testing reg copy of netisr, but
 * interrupt routines setting it would not cause any new problems (since we
 * don't loop, fresh bits will not be processed until the next doreti or spl0).
 */
	testl	$~((1 << NETISR_SCLK) | (1 << NETISR_AST)),%eax
	je	test_ASTs		# no net stuff, just temporary AST's
	FASTSPL_VARMASK(_netmask)
	DONET(NETISR_RAW, _rawintr, 5)

#ifdef	INET
	DONET(NETISR_IP, _ipintr, 6)
#endif	/* INET */

#ifdef	IMP
	DONET(NETISR_IMP, _impintr, 7)
#endif	/* IMP */

#ifdef	NS
	DONET(NETISR_NS, _nsintr, 8)
#endif	/* NS */

#ifdef	ISO
	DONET(NETISR_ISO, _clnlintr, 9)
#endif	/* ISO */

	FASTSPL($0)
test_ASTs:
	btrl	$NETISR_SCLK,_netisr
	jnc	test_resched
	COUNT_EVENT(_intrcnt_spl, 10)
	FASTSPL($SOFTCLOCKMASK)
/*
 * Back to an interrupt frame for a moment.
 */
	pushl	$0			# previous cpl (probably not used)
	pushl	$0x7f			# dummy unit number
	call	_softclock
	addl	$8,%esp			# discard dummies
	FASTSPL($0)
test_resched:
#ifdef notused1
	btrl	$NETISR_AST,_netisr
	jnc	2f
#endif
#ifdef notused2
	cmpl	$0,_want_resched
	je	2f
#endif
	cmpl	$0,_astpending		# XXX - put it back in netisr to
	je	2f			# reduce the number of tests
	testb	$SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
					# to non-kernel (i.e., user)?
	je	2f			# nope, leave
	COUNT_EVENT(_intrcnt_spl, 11)
	movl	$0,_astpending
	call	_trap
2:
	COUNT_EVENT(_intrcnt_spl, 12)
	popl	%es
	popl	%ds
	popal
	addl	$8,%esp
	iret

/*
 * Interrupt priority mechanism
 *	-- soft splXX masks with group mechanism (cpl)
 *	-- h/w masks for currently active or unused interrupts (imen)
 *	-- ipending = active interrupts currently masked by cpl
 */

	GENSPL(bio, _biomask, 13)
	GENSPL(clock, $HIGHMASK, 14)	/* splclock == splhigh ex for count */
	GENSPL(high, $HIGHMASK, 15)
	GENSPL(imp, _netmask, 16)	/* splimp == splnet except for count */
	GENSPL(net, _netmask, 17)
	GENSPL(softclock, $SOFTCLOCKMASK, 18)
	GENSPL(tty, _ttymask, 19)

	.globl _splnone
	.globl _spl0
	ALIGN_TEXT
_splnone:
_spl0:
	COUNT_EVENT(_intrcnt_spl, 20)
in_spl0:
	movl	_cpl,%eax
	pushl	%eax			# save old priority
	testl	$(1 << NETISR_RAW) | (1 << NETISR_IP),_netisr
	je	over_net_stuff_for_spl0
	movl	_netmask,%eax		# mask off those network devices
	movl	%eax,_cpl		# set new priority
	SHOW_CPL
/*
 * XXX - what about other net intrs?
 */
	DONET(NETISR_RAW, _rawintr, 21)

#ifdef	INET
	DONET(NETISR_IP, _ipintr, 22)
#endif	/* INET */

#ifdef	IMP
	DONET(NETISR_IMP, _impintr, 23)
#endif	/* IMP */

#ifdef	NS
	DONET(NETISR_NS, _nsintr, 24)
#endif	/* NS */

#ifdef	ISO
	DONET(NETISR_ISO, _clnlintr, 25)
#endif	/* ISO */

over_net_stuff_for_spl0:
	movl	$0,_cpl			# set new priority
	SHOW_CPL
	movl	_ipending,%eax
	testl	%eax,%eax
	jne	unpend_V
	popl	%eax			# return old priority
	ret

	.globl _splx
	ALIGN_TEXT
_splx:
	COUNT_EVENT(_intrcnt_spl, 26)
	movl	4(%esp),%eax		# new priority
	testl	%eax,%eax
	je	in_spl0			# going to "zero level" is special
	COUNT_EVENT(_intrcnt_spl, 27)
	movl	_cpl,%edx		# save old priority
	movl	%eax,_cpl		# set new priority
	SHOW_CPL
	notl	%eax
	andl	_ipending,%eax
	jne	unpend_V_result_edx
	movl	%edx,%eax		# return old priority
	ret

	ALIGN_TEXT
unpend_V_result_edx:
	pushl	%edx
unpend_V:
	COUNT_EVENT(_intrcnt_spl, 28)
	bsfl	%eax,%eax
	btrl	%eax,_ipending
	jnc	unpend_V_next
	SHOW_IPENDING
	movl	Vresume(,%eax,4),%edx
	testl	%edx,%edx
	je	noresumeV
/*
 * We would prefer to call the intr handler directly here but that doesn't
 * work for badly behaved handlers that want the interrupt frame.  Also,
 * there's a problem determining the unit number.  We should change the
 * interface so that the unit number is not determined at config time.
 */
	jmp	*vec(,%eax,4)

	ALIGN_TEXT
/*
 * XXX - must be some fastintr, need to register those too.
 */
noresumeV:
#if NSIO > 0
	call	_softsio1
#endif
unpend_V_next:
	movl	_cpl,%eax
	notl	%eax
	andl	_ipending,%eax
	jne	unpend_V
	popl	%eax
	ret

#define BUILD_VEC(irq_num) \
	ALIGN_TEXT ; \
vec/**/irq_num: ; \
	int	$ICU_OFFSET + (irq_num) ; \
	popl	%eax ; \
	ret

	BUILD_VEC(0)
	BUILD_VEC(1)
	BUILD_VEC(2)
	BUILD_VEC(3)
	BUILD_VEC(4)
	BUILD_VEC(5)
	BUILD_VEC(6)
	BUILD_VEC(7)
	BUILD_VEC(8)
	BUILD_VEC(9)
	BUILD_VEC(10)
	BUILD_VEC(11)
	BUILD_VEC(12)
	BUILD_VEC(13)
	BUILD_VEC(14)
	BUILD_VEC(15)
