/*
 * Copyright (c) 2018, Synopsys, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#define __ASSEMBLY__
#include "inc/arc/arc.h"
#include "inc/arc/arc_asm_common.h"

.global rt_interrupt_enter; .type rt_interrupt_enter, %function
.global rt_interrupt_leave; .type rt_interrupt_leave, %function
.global context_switch_reqflg; .type context_switch_reqflg, %object
.global rt_interrupt_from_thread; .type rt_interrupt_from_thread, %object
.global rt_interrupt_to_thread; .type rt_interrupt_to_thread, %object

	.text
	.align 4
dispatcher:
	st sp, [r0]
	ld sp, [r1]
	pop r0
	j [r0]

/* return routine when task dispatch happened in task context */
dispatch_r:
	RESTORE_NONSCRATCH_REGS
	j	[blink]

/*
 * rt_base_t rt_hw_interrupt_disable();
 */
.global rt_hw_interrupt_disable
.type rt_hw_interrupt_disable, %function
rt_hw_interrupt_disable:
 	clri r0
 	j [blink]


/*
 * void rt_hw_interrupt_enable(rt_base_t level);
 */
.global rt_hw_interrupt_enable
.type rt_hw_interrupt_enable, %function
rt_hw_interrupt_enable:
	seti r0
	j [blink]


.global rt_hw_context_switch_interrupt
.type rt_hw_context_switch_interrupt, %function
rt_hw_context_switch_interrupt:
	st r0, [rt_interrupt_from_thread]
	st r1, [rt_interrupt_to_thread]
	mov r0, 1
	st r0, [context_switch_reqflg]
	j [blink]


/*
 * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
 * r0 --> from
 * r1 --> to
 */
.global rt_hw_context_switch
.type rt_hw_context_switch, %function
rt_hw_context_switch:
	SAVE_NONSCRATCH_REGS
	mov r2, dispatch_r
	push r2
	b dispatcher


/*
 * void rt_hw_context_switch_to(rt_uint32 to);
 * r0 --> to
 */
.global rt_hw_context_switch_to
.type rt_hw_context_switch_to, %function
rt_hw_context_switch_to:
	ld sp, [r0]
	pop r0
	j [r0]

.global start_r
.type start_r, %function
start_r:
	pop blink;
	pop r1
	pop r2
	pop r0

	j_s.d [r1]
	kflag r2

/****** exceptions and interrupts handing ******/
/****** entry for exception handling ******/
	.global exc_entry_cpu
	.align 4
exc_entry_cpu:

	EXCEPTION_PROLOGUE

	mov	blink,	sp
	mov	r3, sp		/* as exception handler's para(p_excinfo) */

	ld	r0, [exc_nest_count]
	add	r1, r0, 1
	st	r1, [exc_nest_count]
	cmp	r0, 0
	bne	exc_handler_1
/* change to exception stack if interrupt happened in task context */
	mov	sp, _e_stack
exc_handler_1:
	PUSH	blink

	lr	r0, [AUX_ECR]
	lsr	r0, r0, 16
	mov	r1, exc_int_handler_table
	ld.as	r2, [r1, r0]

	mov	r0, r3
	jl	[r2]

/* interrupts are not allowed */
ret_exc:
	POP	sp
	mov	r1, exc_nest_count
	ld	r0, [r1]
	sub	r0, r0, 1
	cmp	r0, 0
	bne.d	ret_exc_1
	st	r0, [r1]

	ld	r0, [context_switch_reqflg]
	cmp	r0, 0
	bne	ret_exc_2
ret_exc_1:	/* return from non-task context, interrupts or exceptions are nested */
	EXCEPTION_EPILOGUE
	rtie

/* there is a dispatch request */
ret_exc_2:
	/* clear dispatch request */
	mov	r0, 0
	st	r0, [context_switch_reqflg]

	SAVE_CALLEE_REGS	/* save callee save registers */

	/* clear exception bit to do exception exit by SW */
	lr	r0, [AUX_STATUS32]
	bclr	r0, r0, AUX_STATUS_BIT_AE
	kflag	r0

	mov	r1, ret_exc_r	/* save return address */
	PUSH	r1

	ld 	r0, [rt_interrupt_from_thread]
	ld 	r1, [rt_interrupt_to_thread]
	b	dispatcher

ret_exc_r:
	/* recover exception status */
	lr	r0, [AUX_STATUS32]
	bset	r0, r0, AUX_STATUS_BIT_AE
	kflag	r0

	RESTORE_CALLEE_REGS
	EXCEPTION_EPILOGUE
	rtie

/****** entry for normal interrupt exception handling ******/
	.global exc_entry_int	/* entry for interrupt handling */
	.align 4
exc_entry_int:
#if ARC_FEATURE_FIRQ == 1
/*  check whether it is P0 interrupt */
#if ARC_FEATURE_RGF_NUM_BANKS > 1
	lr	r0, [AUX_IRQ_ACT]
	btst	r0, 0
	jnz	exc_entry_firq
#else
	PUSH	r10
	lr	r10, [AUX_IRQ_ACT]
	btst	r10, 0
	POP	r10
	jnz	exc_entry_firq
#endif
#endif
	INTERRUPT_PROLOGUE

	mov	blink, sp

	clri	/* disable interrupt */
	ld	r3, [exc_nest_count]
	add	r2, r3, 1
	st	r2, [exc_nest_count]
	seti	/* enable higher priority interrupt */

	cmp	r3, 0
	bne	irq_handler_1
/* change to exception stack if interrupt happened in task context */
	mov	sp, _e_stack
irq_handler_1:
	PUSH	blink

	jl 	rt_interrupt_enter

	lr	r0, [AUX_IRQ_CAUSE]
	sr	r0, [AUX_IRQ_SELECT]
	mov	r1, exc_int_handler_table
	ld.as	r2, [r1, r0]	/* r2 = exc_int_handler_table + irqno *4 */
/* handle software triggered interrupt */
	lr	r3, [AUX_IRQ_HINT]
	cmp	r3, r0
	bne.d irq_hint_handled
	xor	r3, r3, r3
	sr	r3, [AUX_IRQ_HINT]
irq_hint_handled:
	lr	r3, [AUX_IRQ_PRIORITY]
	PUSH	r3		/* save irq priority */

	jl	[r2]		/* jump to interrupt handler */
	jl	rt_interrupt_leave
ret_int:
	clri			/* disable interrupt */
	POP	r3		/* irq priority */
	POP	sp
	mov	r1, exc_nest_count
	ld	r0, [r1]
	sub	r0, r0, 1
	cmp	r0, 0
	bne.d	ret_int_1
	st	r0, [r1]

	ld	r0, [context_switch_reqflg]
	cmp	r0, 0
	bne	ret_int_2
ret_int_1:	/* return from non-task context */
	INTERRUPT_EPILOGUE
	rtie
/* there is a dispatch request */
ret_int_2:
	/* clear dispatch request */
	mov	r0, 0
	st	r0, [context_switch_reqflg]

	/* interrupt return by SW */
	lr	r10, [AUX_IRQ_ACT]
	PUSH	r10
	bclr	r10, r10, r3	/* clear related bits in IRQ_ACT */
	sr	r10, [AUX_IRQ_ACT]

	SAVE_CALLEE_REGS	/* save callee save registers */
	mov	r1, ret_int_r	/* save return address */
	PUSH	r1

	ld 	r0, [rt_interrupt_from_thread]
	ld 	r1, [rt_interrupt_to_thread]
	b	dispatcher

ret_int_r:
	RESTORE_CALLEE_REGS
	/* recover AUX_IRQ_ACT to restore the interrup status */
	POPAX	AUX_IRQ_ACT
	INTERRUPT_EPILOGUE
	rtie