/*
 * FreeRTOS Kernel V11.3.0
 * Copyright (C) 2020 Synopsys, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/**
 * \file
 * \ingroup OS_FREERTOS
 * \brief  freertos support for arc processor
 *  like task dispatcher, interrupt handler
 */
/** @cond OS_FREERTOS_ASM_ARC_SUPPORT */

/*
 * core-dependent part in assemble language (for arc)
 */
#define __ASSEMBLY__
#include "arc/arc.h"
#include "arc/arc_asm_common.h"

/*
 *  task dispatcher
 *
 */
    .text
    .align 4
    .global dispatch
dispatch:
/*
 *  the pre-conditions of this routine are task context, CPU is
 *  locked, dispatch is enabled.
 */
    SAVE_NONSCRATCH_REGS        /* save callee save registers */
    mov r1, dispatch_r
    PUSH    r1          /* save return address */
    ld  r0, [pxCurrentTCB]
    bl  dispatcher

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

/*
 *  start dispatch
 */
    .global start_dispatch
    .align 4
start_dispatch:
/*
 *  this routine is called in the non-task context during the startup of the kernel
 *  , and all the interrupts are locked.
 *
 *  when the dispatcher is called, the cpu is locked, no nest exception (CPU exception/interrupt).
 *  In target_initialize, all interrupt priority mask should be cleared, cpu should be
 *  locked, the interrupts outside the kernel such as fiq can be
 *  enabled.
 */
    clri
    mov r0, 0
    st  r0, [exc_nest_count]
    b   dispatcher_0
/*
 *  dispatcher
 */
dispatcher:
    ld  r1, [ulCriticalNesting]
    PUSH    r1          /* save critical nesting */
    st  sp, [r0]        /* save stack pointer of current task, r0->pxCurrentTCB */
    jl  vTaskSwitchContext  /* change the value of pxCurrentTCB */
/*
 *  before dispatcher is called, task context | cpu locked | dispatch enabled
 *  should be satisfied. In this routine, the processor will jump
 *  into the entry of next to run task
 *
 *  i.e. kernel mode, IRQ disabled, dispatch enabled
 */
dispatcher_0:
    ld  r1, [pxCurrentTCB]
    ld  sp, [r1]    /* recover task stack */
#if ARC_FEATURE_STACK_CHECK
    lr r0, [AUX_STATUS32]
    bclr r0, r0, AUX_STATUS_BIT_SC
    flag r0
    jl  vPortSetStackCheck
    lr r0, [AUX_STATUS32]
    bset r0, r0, AUX_STATUS_BIT_SC
    flag r0
#endif
    POP r0      /* get critical nesting */
    st  r0, [ulCriticalNesting]
    POP r0      /* get return address  */
    j   [r0]

/*
 *  task startup routine
 *
 */
    .text
    .global start_r
    .align 4
start_r:
    seti    /* unlock cpu */
    mov blink, vPortEndTask /* set return address */
    POP r1          /* get task function body */
    POP r0          /* get task parameters */
    j   [r1]

/****** 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  r1, [exc_nest_count]
    add r1, r1, 1
    st  r1, [exc_nest_count]
    brne    r1, 0, exc_handler_1
/* change to exception stack if interrupt happened in task context */
    mov sp, _e_stack
exc_handler_1:
    PUSH    blink

/* find the exception cause */
#if ARC_FEATURE_CORE_700
    lr  r0, [AUX_ECR]
    lsr r0, r0, 16
    bmsk    r0, r0, 7
#endif
    mov r1, exc_int_handler_table
    ld.as   r2, [r1, r0]

    mov r0, r3
    jl  [r2]        /* !!!!jump to exception handler where interrupts are not allowed! */

/* interrupts are not allowed */
ret_exc:
    POP sp
    mov r1, exc_nest_count
    ld  r0, [r1]
    sub r0, r0, 1
    st  r0, [r1]
    brne    r0, 0, ret_exc_1  /* nested exception case */
    lr  r1, [AUX_IRQ_LV12]
    brne    r1, 0, ret_exc_1  /* nested or pending interrupt case */

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

    EXCEPTION_EPILOGUE
#if ARC_FEATURE_CORE_600
    rtie ilink2
#else
    rtie
#endif

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

    ld  r0, [pxCurrentTCB]
    breq    r0, 0, ret_exc_1

    SAVE_CALLEE_REGS    /* save callee save registers */

    lr  r0, [AUX_STATUS32]
    bclr    r0, r0, AUX_STATUS_BIT_AE   /* clear exception bit */
    flag    r0

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

    bl  dispatcher  /* r0->pxCurrentTCB */

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

    RESTORE_CALLEE_REGS /* recover registers */
    EXCEPTION_EPILOGUE
#if ARC_FEATURE_CORE_600
    rtie ilink2
#else
    rtie
#endif

/****** entry for normal interrupt exception handling ******/
    .global exc_entry_int   /* entry for interrupt handling */
    .align 4
exc_entry_int:

    INTERRUPT_PROLOGUE

    mov blink, sp

    /* disable interrupt */
    push r0
    lr  r0, [AUX_STATUS32]
    push r0
    bclr r0, r0, AUX_STATUS_BIT_E1
    bclr r0, r0, AUX_STATUS_BIT_E2
    flag r0
    ld  r3, [exc_nest_count]
    add r2, r3, 1
    st  r2, [exc_nest_count]
    /* enable interrupt */
    pop r0
    flag r0
    pop r0

    brne    r3, 0, irq_handler_1
/* change to exception stack if interrupt happened in task context */
    mov sp, _e_stack
#if ARC_FEATURE_STACK_CHECK
    lr r0, [AUX_STATUS32]
    bclr r0, r0, AUX_STATUS_BIT_SC
    flag r0
#endif
irq_handler_1:
    PUSH    blink

/* critical area */
#if ARC_FEATURE_CORE_700
    lr  r0, [AUX_IRQ_CAUSE1]
#endif
    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:

    jl  [r2]        /* jump to interrupt handler */
/* no interrupts are allowed from here */
ret_int:
    clri    /* disable interrupt */

    POP sp
    mov r1, exc_nest_count
    ld  r0, [r1]
    sub r0, r0, 1
    st  r0, [r1]
/* if there are multi-bits set in IRQ_LV12, it's still in nest interrupt */
    lr  r1, [AUX_IRQ_LV12]

    ld  r0, [context_switch_reqflg]
    brne    r0, 0, ret_int_2
ret_int_1:  /* return from non-task context */
    INTERRUPT_EPILOGUE
#if ARC_FEATURE_CORE_600
/* TODO: series 600 IRQ6 and IRQ7 uses ilink2 */
    rtie ilink1
#else
    rtie
#endif
/* there is a dispatch request */
ret_int_2:
    /* clear dispatch request */
    mov r0, 0
    st  r0, [context_switch_reqflg]

    ld  r0, [pxCurrentTCB]
    breq    r0, 0, ret_int_1

/* r1 has old AUX_IRQ_LV12 */
    PUSH    r1
/* clear related bits in IRQ_ACT manually to simulate a irq return  */

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

    bl  dispatcher  /* r0->pxCurrentTCB */

ret_int_r:
    RESTORE_CALLEE_REGS /* recover registers */
    POPAX   AUX_IRQ_LV12
    INTERRUPT_EPILOGUE
#if ARC_FEATURE_CORE_600
    rtie ilink1
#else
    rtie
#endif

/** @endcond */
