/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#if defined(lint)
#include <sys/types.h>
#include <sys/thread.h>
#else	/* lint */
#include "assym.h"
#endif	/* lint */

#include <sys/asm_linkage.h>
#include <sys/machthread.h>
#include <sys/machcpuvar.h>
#include <sys/mmu.h>
#include <sys/intreg.h>
#include <sys/dmv.h>

#ifdef TRAPTRACE
#include <sys/traptrace.h>
#endif /* TRAPTRACE */


#if defined(lint)

void
vec_interrupt(void)
{}

#else	/* lint */

vec_uiii_irdr_tab:
        .byte   UIII_IRDR_0, UIII_IRDR_1, UIII_IRDR_2, UIII_IRDR_3
        .byte   UIII_IRDR_4, UIII_IRDR_5, UIII_IRDR_6, UIII_IRDR_7

/*
 * (TT 0x60, TL>0) Interrupt Vector Handler
 *	Globals are the Interrupt Globals.
 */
	ENTRY_NP(vec_interrupt)
	!
	! Load the interrupt receive data register 0.
	! It could be a fast trap handler address (pc > KERNELBASE) at TL>0
	! or an interrupt number.
	!
	mov	IRDR_0, %g2
	ldxa	[%g2]ASI_INTR_RECEIVE, %g5	! %g5 = PC or Interrupt Number

	! If the high bit of IRDR_0 is set, then this is a
	! data bearing mondo vector.
	brlz,pt %g5, dmv_vector
	.empty


vec_interrupt_resume:	
	set	KERNELBASE, %g4
	cmp	%g5, %g4
	bl,a,pt	%xcc, 0f			! an interrupt number found
	  nop
	!
	! intercept OBP xcalls and set PCONTEXT=0
	!
	set	_end, %g4		! _end is highest kernel address
	cmp	%g5, %g4
	bl,a,pt	%xcc, 7f
	  nop
	mov	MMU_PCONTEXT, %g1
	ldxa	[%g1]ASI_DMMU, %g1
	srlx	%g1, CTXREG_NEXT_SHIFT, %g3
	brz,pt	%g3, 7f			! nucleus pgsz is 0, no problem
	  sllx	%g3, CTXREG_NEXT_SHIFT, %g3
	set	CTXREG_CTX_MASK, %g4	! check Pcontext
	btst	%g4, %g1
	bz,a,pt	%xcc, 6f
	  clr	%g3			! kernel:  PCONTEXT=0
	xor	%g3, %g1, %g3		! user:	clr N_pgsz0/1 bits
6:
	set	DEMAP_ALL_TYPE, %g1
	stxa	%g0, [%g1]ASI_DTLB_DEMAP
	stxa	%g0, [%g1]ASI_ITLB_DEMAP
	mov	MMU_PCONTEXT, %g1
	stxa	%g3, [%g1]ASI_DMMU
        membar  #Sync
	sethi	%hi(FLUSH_ADDR), %g1
	flush	%g1			! flush required by immu
7:
	!
	!  Cross-trap request case
	!
	! Load interrupt receive data registers 1 and 2 to fetch
	! the arguments for the fast trap handler.
	!
	! Register usage:
	!	g5: TL>0 handler
	!	g1: arg1
	!	g2: arg2
	!	g3: arg3
	!	g4: arg4
	!
	mov	IRDR_1, %g2
	ldxa	[%g2]ASI_INTR_RECEIVE, %g1
	mov	IRDR_2, %g2
	ldxa	[%g2]ASI_INTR_RECEIVE, %g2
#ifdef TRAPTRACE
	TRACE_PTR(%g4, %g6)
	GET_TRACE_TICK(%g6)
	stxa	%g6, [%g4 + TRAP_ENT_TICK]%asi
	rdpr	%tl, %g6
	stha	%g6, [%g4 + TRAP_ENT_TL]%asi
	rdpr	%tt, %g6
	stha	%g6, [%g4 + TRAP_ENT_TT]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g4 + TRAP_ENT_TPC]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g4 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g4 + TRAP_ENT_SP]%asi
	stna	%g5, [%g4 + TRAP_ENT_TR]%asi	! pc of the TL>0 handler
	stxa	%g1, [%g4 + TRAP_ENT_F1]%asi
	stxa	%g2, [%g4 + TRAP_ENT_F3]%asi
	stxa	%g0, [%g4 + TRAP_ENT_F2]%asi
	stxa	%g0, [%g4 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g4, %g6, %g3)
#endif /* TRAPTRACE */
	stxa	%g0, [%g0]ASI_INTR_RECEIVE_STATUS	! clear the BUSY bit
	membar	#Sync
#ifdef SF_ERRATA_51
	ba,pt	%icc, 1f
	nop
	.align 32
1:	jmp	%g5				! call the fast trap handler
	nop
#else
	jmp	%g5
	nop
#endif /* SF_ERRATA_51 */
	/* Never Reached */

0:
	! We have an interrupt number.
        !
	! Register usage:
	!	%g5 - inum
	!	%g1 - temp
	!
        ! We don't bother to verify that the received inum is valid (it should
        ! be < MAXIVNUM) since setsoftint_tl1 will do that for us.
        !
	! clear BUSY bit
	!
	stxa	%g0, [%g0]ASI_INTR_RECEIVE_STATUS
	membar	#Sync

	! setsoftint_tl1 will do all the work, and finish with a retry
	!
	ba,pt	%xcc, setsoftint_tl1
	  mov	%g5, %g1		! setsoftint_tl1 expects inum in %g1

	/* Never Reached */
	SET_SIZE(vec_interrupt)


!	
!   See usr/src/uts/sun4u/sys/dmv.h for the Databearing Mondo Vector
!	 interrupt format
!
! Inputs:
!	g1: value of ASI_INTR_RECEIVE_STATUS
!	g5: word 0 of the interrupt data
! Register use:
!	g2: dmv inum
!	g3: scratch
!	g4: pointer to dmv_dispatch_table
!	g6: handler pointer from dispatch table


	DGDEF(dmv_spurious_cnt)
	.word	0

	ENTRY_NP(dmv_vector)
	srlx	%g5, DMV_INUM_SHIFT, %g2
	set	DMV_INUM_MASK, %g3
	and	%g2, %g3, %g2		   ! %g2 = inum

	set	dmv_totalints, %g3
	ld	[%g3], %g3
	cmp	%g2, %g3
	bge,pn	%xcc, 2f		   ! inum >= dmv_totalints
	nop
	
	set	dmv_dispatch_table, %g3
	ldn	[%g3], %g4
	brz,pn	%g4, 2f
	sll	%g2, DMV_DISP_SHIFT, %g3   ! %g3 = inum*sizeof(struct dmv_disp)
		
	add	%g4, %g3, %g4		! %g4 = &dmv_dispatch_table[inum]
#if (DMV_FUNC != 0) || (DMV_ARG != 8)
#error "DMV_FUNC or DMV_SIZE has changed"
#endif
	ldda	[%g4]ASI_NQUAD_LD, %g2  ! %g2=handler %g3=argument
	mov	%g3, %g1
	brz,pn  %g2, 2f	
	nop
	
	! we have a handler, so call it
	! On entry to the handler, the %g registers are set as follows:
	!
	!	%g1	The argument (arg) passed to dmv_add_intr().
	!	%g2	Word 0 of the incoming mondo vector.
	!
	jmp	%g2
	mov	%g5, %g2
		
	! No handler was listed in the table, so just record it
	! as an error condition and continue.  There is a race
	! window here updating the counter, but that's ok since
	! just knowing that spurious interrupts happened is enough,
	! we probably won't need to know exactly how many.
2:
	set	dmv_spurious_cnt, %g1
	ld	[%g1], %g2
	inc	%g2
	ba,pt	%xcc,3f
	st	%g2, [%g1]
	
	!	When the handler's processing (which should be as quick as
	!	possible) is complete, the handler must exit by jumping to
	!	the label dmv_finish_intr.  The contents of %g1 at this time
	!	determine whether a software interrupt will be issued, as
	!	follows:
	!
	!		If %g1 is less than zero, no interrupt will be queued.
	!		Otherwise, %g1 will be used as the interrupt number
	!		to simulate; this means that the behavior of the
	!		interrupt system will be exactly that which would have
	!		occurred if the first word of the incoming interrupt
	!		vector had contained the contents of %g1.

	ENTRY_NP(dmv_finish_intr)
	brlz,pn %g1,3f
	nop
	!	generate an interrupt based on the contents of %g1
	ba,pt	%xcc,vec_interrupt_resume
	mov	%g1, %g5
	!	We are done
3:	
	stxa	%g0, [%g0]ASI_INTR_RECEIVE_STATUS ! clear the busy bit
	retry
	SET_SIZE(dmv_vector)

#endif	/* lint */

#if defined(lint)

void
vec_intr_spurious(void)
{}

#else	/* lint */

	DGDEF(vec_spurious_cnt)
	.word	0

	ENTRY_NP(vec_intr_spurious)
	sethi	%hi(vec_spurious_cnt), %g2
	ld	[%g2 + %lo(vec_spurious_cnt)], %g2
#ifdef TRAPTRACE
	TRACE_PTR(%g4, %g6)
	GET_TRACE_TICK(%g6)
	stxa	%g6, [%g4 + TRAP_ENT_TICK]%asi
	rdpr	%tl, %g6
	stha	%g6, [%g4 + TRAP_ENT_TL]%asi
	rdpr	%tt, %g6
	or	%g6, TT_SPURIOUS_INT, %g6
	stha	%g6, [%g4 + TRAP_ENT_TT]%asi
	rdpr	%tpc, %g6
	stna	%g6, [%g4 + TRAP_ENT_TPC]%asi
	rdpr	%tstate, %g6
	stxa	%g6, [%g4 + TRAP_ENT_TSTATE]%asi
	stna	%sp, [%g4 + TRAP_ENT_SP]%asi
	stna	%g1, [%g4 + TRAP_ENT_TR]%asi	! irsr
	stna	%g2, [%g4 + TRAP_ENT_F1]%asi
	ldxa	[%g0]ASI_INTR_RECEIVE_STATUS, %g5
	stxa	%g5, [%g4 + TRAP_ENT_F2]%asi
	stxa	%g0, [%g4 + TRAP_ENT_F4]%asi
	TRACE_NEXT(%g4, %g6, %g3)
#endif /* TRAPTRACE */
	cmp	%g2, 16
	bl,a,pt	%xcc, 1f
	inc	%g2
	!
	! prepare for sys_trap()
	!	%g1 - sys_tl1_panic
	!	%g2 - panic message
	!	%g4 - current pil
	!
	sub	%g0, 1, %g4
	set	_not_ready, %g2
	sethi	%hi(sys_tl1_panic), %g1
	ba,pt	%xcc, sys_trap
	or	%g1, %lo(sys_tl1_panic), %g1
	!
1:	sethi	%hi(vec_spurious_cnt), %g1
	st	%g2, [%g1 + %lo(vec_spurious_cnt)]
	retry
	SET_SIZE(vec_intr_spurious)

_not_ready:	.asciz	"Interrupt Vector Receive Register not READY"

#endif	/* lint */
