/***********************************************************************
*
*             Copyright 2011 Mentor Graphics Corporation
*                         All Rights Reserved.
*
* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
* SUBJECT TO LICENSE TERMS.
*
************************************************************************

************************************************************************
*
*   FILE NAME
*
*       mips.c
*
*   DESCRIPTION
*
*       This file contains the core MIPS architecture functions
*
*   FUNCTIONS
*
*       ESAL_AR_STK_Unsolicited_Set
*       ESAL_TS_STK_Solicited_Set
*       ESAL_TS_MEM_First_Avail_Get
*       ESAL_TS_MEM_BSS_Clear
*       ESAL_TS_MEM_ROM_To_RAM_Copy
*       ESAL_AR_ISR_Initialize
*       ESAL_AR_ISR_Vector_Table_Install
*       ESAL_AR_ISR_Return
*       ESAL_AR_ISR_Special_Handler
*       ESAL_AR_INT_Enable
*       ESAL_AR_INT_Disable
*       ESAL_AR_TMR_OS_Timer_Start
*       ESAL_CO_MEM_Cache_Enable
*
*   DEPENDENCIES
*
*       nucleus.h                           Nucleus System constants
*
***********************************************************************/

/* Include required header files */
#include            "nucleus.h"

/* Define externally referenced functions */
extern VOID                 ESAL_AR_ISR_Vector_Table(VOID);
extern VOID                 ESAL_AR_ISR_Vector_Table_End(VOID);

#if (ESAL_AR_ISR_INIT_REQUIRED == NU_TRUE)

/* Define local functions */
static VOID                 ESAL_AR_ISR_Special_Handler(INT);

/* Define local variables */
static UINT32               ESAL_AR_ISR_Spurious_Count;

/* This array contains all the architecture interrupts in order of priority */
static ESAL_AR_ISR_DATA     ESAL_AR_ISR_Priority[ESAL_AR_ISR_PRIORITY_ARRAY_MAX] = 
{
                            /* INTERRUPT VECTOR ID      /   INITIAL MASK VALUE */
                            {ESAL_AR_HW5_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_HW4_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_HW3_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_HW2_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_HW1_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_HW0_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_SW1_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK},
                            {ESAL_AR_SW0_INT_VECTOR_ID,   ESAL_AR_ISR_INITIAL_MASK}
};

#endif /* ESAL_AR_ISR_INIT_REQUIRED */

#if (ESAL_AR_OS_TIMER_USED == NU_TRUE)

/* Global OS counter variable */
UINT32                      ESAL_AR_TMR_OS_Count;

#endif /* ESAL_AR_OS_TIMER_USED */


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_STK_Unsolicited_Set
*
*   DESCRIPTION
*
*       This function populates a stack frame as required by the given
*       architecture (contains all registers that must be preserved
*       across an interrupt context switch).
*
*   CALLED BY
*
*       Operating System Services
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       start_addr                          Start address of stack mem
*       end_addr                            End address of stack mem
*       entry_function                      Address of execution entry
*
*   OUTPUTS
*
*       Stack Frame Stack Pointer
*
***********************************************************************/
VOID    *ESAL_AR_STK_Unsolicited_Set(VOID *start_addr, VOID *end_addr, 
                                     VOID (*entry_function)(VOID))
{
    NU_REG ESAL_AR_STK    *stack_frame;
    INT                     current_sr;


    /* Set address of interrupt stack frame */
    stack_frame = (ESAL_AR_STK *)((VOID_CAST)end_addr - ESAL_GE_STK_MAX_FRAME_SIZE);

    /* Set stack type within stack frame */
    stack_frame -> stack_type = ESAL_GE_STK_AR_TYPE;

    /* Get current CP0 status register */
    ESAL_TS_RTE_CP_READ(0, &current_sr, ESAL_TS_RTE_CPR12, 0);

    /* Save the current status register with the IE bit enabled */
    (stack_frame->min_stack).sr = (current_sr | ESAL_AR_INTERRUPTS_ENABLE_BITS);

    /* Set return address / entry point within stack frame */
    (stack_frame -> min_stack).rtn_address = (UINT32)entry_function;

    /* Zeroize r31 within stack frame (provides nicer debug environment) */
    (stack_frame -> min_stack).r31 = 0;

    /* Return stack pointer to caller */
    return ((VOID *)stack_frame);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_STK_Solicited_Set
*
*   DESCRIPTION
*
*       This function populates a solicited stack frame as required
*       by the toolset for this architecture.  A solicited stack
*       frame is one that contains all the registers required by
*       the given toolset to be preserved across a function call
*       boundary (i.e. registers that must be unchanged after making
*       a function call within C code).
*
*   CALLED BY
*
*       Operating System Services
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       start_addr                          Start address of stack mem
*       end_addr                            End address of stack mem
*       entry_function                      Address of exec. entry
*
*   OUTPUTS
*
*       Stack Frame Stack Pointer
*
***********************************************************************/
VOID    *ESAL_TS_STK_Solicited_Set(VOID *start_addr, VOID *end_addr,
                                   VOID (*entry_function)(VOID))
{
    NU_REG ESAL_TS_STK     *stack_frame;


    /* Set address of stack frame */
    stack_frame = (ESAL_TS_STK *)((UINT)end_addr - ESAL_GE_STK_MIN_FRAME_SIZE);

    /* Set stack type within stack frame */
    stack_frame -> stack_type = ESAL_GE_STK_TS_TYPE;

    /* Set return address / entry point within stack frame */
    stack_frame -> rtn_address = (UINT)entry_function;

    /* Return stack pointer to caller */
    return ((VOID *)stack_frame);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_MEM_First_Avail_Get
*
*   DESCRIPTION
*
*       This function returns the first available RAM memory address
*       available for use.  This value is usually obtained from
*       linker produced addresses and labels and it is a static value
*       (i.e. it won't be updated at run-time to reflect use of available
*       memory - it only represents the first available memory
*       address that existed at link time)
*
*   CALLED BY
*
*       ESAL_GE_MEM_Initialize
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       VOID *                              Pointer to first available
*                                           RAM memory usable
*
***********************************************************************/
VOID    *ESAL_TS_MEM_First_Avail_Get(VOID)
{
    /* Return the address of the first memory available for use
       by an application. */
    return (TOOLSET_BSS_END_ADDR);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_MEM_BSS_Clear
*
*   DESCRIPTION
*
*       This function clears the BSS for the given toolset
*
*   CALLED BY
*
*       ESAL_GE_RTE_Initialize
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_TS_MEM_BSS_Clear(VOID)
{
    /* Clear all BSS / Zero-initialized memory */
    ESAL_GE_MEM_Clear(TOOLSET_BSS_START_ADDR, TOOLSET_BSS_SIZE);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_TS_MEM_ROM_To_RAM_Copy
*
*   DESCRIPTION
*
*       This function copies initialized data from ROM to RAM for the
*       given toolset
*
*   CALLED BY
*
*       ESAL_GE_RTE_Initialize
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_TS_MEM_ROM_To_RAM_Copy(VOID)
{
#if (( CFG_NU_OS_KERN_PLUS_CORE_ROM_SUPPORT == NU_TRUE ) || ( ESAL_TS_ROM_TO_RAM_COPY_SUPPORT == NU_TRUE ))

    /* Check destination address (RAM data start) is not equal to 
       source address (ROM data start). */
    if (TOOLSET_DATA_DST_ADDR != TOOLSET_DATA_SRC_ADDR)
    {
        /* Copy initialized data from ROM to RAM */
        ESAL_GE_MEM_Copy(TOOLSET_DATA_DST_ADDR, TOOLSET_DATA_SRC_ADDR, TOOLSET_DATA_SIZE);
    }

#endif
}

#if (ESAL_AR_ISR_INIT_REQUIRED == NU_TRUE)

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_ISR_Initialize
*
*   DESCRIPTION
*
*       This function performs any interrupt servicing related 
*       initialization  necessary for the given architecture.  
*       This includes setting-up interrupt related stacks, initializing
*       data used during interrupt handling, etc
*
*   CALLED BY
*
*       ESAL_GE_ISR_Initialize
*
*   CALLS
*   
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_AR_ISR_Initialize(VOID)
{
    INT      i;
    INT      vector;
    UINT32   prev_mask = ESAL_AR_ISR_INITIAL_MASK;


    /* Initializing the Cause Register */
    ESAL_TS_RTE_CP_WRITE(0, ESAL_AR_ISR_CR_IV_BIT, ESAL_TS_RTE_CPR13, 0);
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SYNC_EHB_EXECUTE();

    /* Register the special interrupt handler */
    ESAL_GE_ISR_HANDLER_SET(ESAL_AR_SPECIAL_INT_VECTOR_ID, ESAL_AR_ISR_Special_Handler);

    /* Initialize priority mask values to allow nesting of hardware
       and software interrupts (start with last vector in table). */
    for (i = (ESAL_AR_ISR_PRIORITY_ARRAY_MAX - 1); i >= 0; --i)
    {
        /* Get interrupt vector ID */
        vector = ESAL_AR_ISR_Priority[i].int_vector_id;

        /* Build mask value */
        ESAL_AR_ISR_Priority[i].priority_mask = prev_mask;

        /* Update previous mask */
        prev_mask &= ~(1UL << (vector + ESAL_AR_ISR_CR_IP0_BIT_POS));
    }
}

#endif  /* ESAL_AR_ISR_INIT_REQUIRED */

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_ISR_Vector_Table_Install
*
*   DESCRIPTION
*
*       This function installs the vector table as required for the
*       given architecture
*
*   CALLED BY
*
*       ESAL_GE_ISR_Initialize
*
*   CALLS
*
*       ESAL_GE_MEM_Copy
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_AR_ISR_Vector_Table_Install(VOID)
{
#if ESAL_TS_RTE_MIPS_ISA_REV >= 2

    /* Set co-processor 0 register 15 to vector table address. */
    ESAL_TS_RTE_CP_WRITE(0, &ESAL_AR_ISR_Vector_Table, ESAL_TS_RTE_CPR15, 1);

#else

    UINT32  size;


    /* Check if the hardware vector table is linked at the required location */
    if ((VOID_CAST)&ESAL_AR_ISR_Vector_Table != (VOID_CAST)ESAL_AR_ISR_VECTOR_TABLE_DEST_ADDR)
    {
        /* Calculate the size of area being copied. */
        size = (VOID_CAST)&ESAL_AR_ISR_Vector_Table_End - (VOID_CAST)&ESAL_AR_ISR_Vector_Table;
        
        /* Copy vector table from linked position to the processor
           specified location */
        ESAL_GE_MEM_Copy((VOID *)ESAL_AR_ISR_VECTOR_TABLE_DEST_ADDR, 
                         ESAL_AR_ISR_Vector_Table, 
                         size);

        /* Invalidate the instruction cache */
        ESAL_CO_MEM_ICACHE_ALL_INVALIDATE();

        /* Flush and invalidate the data cache */
        ESAL_CO_MEM_DCACHE_ALL_FLUSH_INVAL();
    }

#endif  /* ESAL_TS_RTE_MIPS_ISA_REV >= 2 */

}


#if (ESAL_AR_ISR_RTI_MANDATORY == NU_TRUE)

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_ISR_Return
*
*   DESCRIPTION
*
*       This function performs a "return from interrupt" or similar
*       architecture specific instruction to return the requested
*       function after servicing an interrupt.  
*
*   CALLED BY
*
*       OS Services
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       rtn_func_ptr                        Address of function to
*                                           return to
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_AR_ISR_Return(VOID (*rtn_func_ptr)(VOID))
{
#error  The MIPS32 architecture requires no return from interrupt.  Check esal_ar_cfg.h settings.
}

#endif  /* ESAL_AR_ISR_RTI_MANDATORY */


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_ISR_Special_Handler
*
*   DESCRIPTION
*
*       This function finds the highest priority interrupt to handle
*
*   CALLED BY
*
*       ESAL_GE_ISR_Initialize
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       int_vector_id                  holds the vector number value
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
static VOID    ESAL_AR_ISR_Special_Handler(INT int_vector_id)
{
    UINT32         status_reg;
    UINT32         cause_reg;
    UINT32         ip_bits;
    UINT32         temp_sr;
    INT            vector;
    INT            i;


    /* Get Status Register (SR) value and Cause register (CR) value */
    ESAL_TS_RTE_CP_READ(0, &status_reg, ESAL_TS_RTE_CPR12, 0);
    ESAL_TS_RTE_CP_READ(0, &cause_reg, ESAL_TS_RTE_CPR13, 0);

    /* Clear non-interrupt bits */
    cause_reg &= ESAL_AR_ISR_SR_IM_MASK;

    /* Only accept the interrupts that are enabled in SR */
    ip_bits = (cause_reg & status_reg);

    /* Only retain IP values */
    ip_bits >>= ESAL_AR_ISR_CR_IP0_BIT_POS;

    /* Check for spurious interrupt */
    if (ip_bits)
    {
        /* Check for the highest priority pending interrupt */
        for (i=0; i < ESAL_AR_ISR_PRIORITY_ARRAY_MAX; ++i)
        {
            /* Get vector number */
            vector = ESAL_AR_ISR_Priority[i].int_vector_id;
        
            /* Check if interrupt is pending */
            if (ip_bits & (1UL << vector))
            {
                break;
            }
        }

        /* Clear EXL and IE bit in saved status register */
        status_reg &= ~(ESAL_AR_ISR_SR_EXL_BIT | ESAL_AR_INTERRUPTS_ENABLE_BITS);
        
        /* Mask all lower priority interrupts in status register */
        temp_sr = (status_reg & ESAL_AR_ISR_Priority[i].priority_mask);

        /* Set new status */
        ESAL_TS_RTE_CP_WRITE(0, temp_sr, ESAL_TS_RTE_CPR12, 0);
        ESAL_TS_RTE_SSNOP_EXECUTE();
        ESAL_TS_RTE_SSNOP_EXECUTE();
        ESAL_TS_RTE_SYNC_EHB_EXECUTE();
        
        /* Execute ISR */
        ESAL_GE_ISR_HANDLER_EXECUTE(vector);

        /* Restore saved SR */
        ESAL_TS_RTE_CP_WRITE(0, status_reg, ESAL_TS_RTE_CPR12, 0);
        ESAL_TS_RTE_SSNOP_EXECUTE();
        ESAL_TS_RTE_SSNOP_EXECUTE();
        ESAL_TS_RTE_SYNC_EHB_EXECUTE();
    }
    else
    {
        /* Increment Spurious Count value */
        ESAL_AR_ISR_Spurious_Count++;
    }
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_INT_Enable
*
*   DESCRIPTION
*
*       This function enables the interrupt source on the architecture
*       that is associated with the specified vector ID
*
*   CALLED BY
*
*       ESAL_GE_INT_Enable
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       vector_id                           vector ID of interrupt
*                                           source being enabled
*       trigger_type                        Trigger method for given
*                                           interrupt vector ID
*       priority                            Priority for the given
*                                           interrupt vector ID
*
*   OUTPUTS
*
*       INT                                 vector_id if passed
*                                           ESAL_DP_INT_VECTOR_ID_DELIMITER
*                                           if failed
*
***********************************************************************/
INT     ESAL_AR_INT_Enable(INT                     vector_id,
                           ESAL_GE_INT_TRIG_TYPE   trigger_type,
                           INT                     priority)
{
    UINT sr_tmp;


    /* Read status register */
    ESAL_TS_RTE_CP_READ(0, &sr_tmp, ESAL_TS_RTE_CPR12, 0);

    /* Get vector_id register bit and set vector_id bit
       value in status register */
    sr_tmp |= (1UL << (vector_id + ESAL_AR_ISR_CR_IP0_BIT_POS));

    /* Write new status value */
    ESAL_TS_RTE_CP_WRITE(0, sr_tmp, ESAL_TS_RTE_CPR12, 0);
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SYNC_EHB_EXECUTE();

    /* Return the vector ID */
    return (vector_id);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_INT_Disable
*
*   DESCRIPTION
*
*       This function disables the interrupt source on the architecture
*       that is associated with the specified vector ID
*
*   CALLED BY
*
*       ESAL_GE_INT_Disable
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       vector_id                           Vector ID of interrupt
*                                           source being disabled
*
*   OUTPUTS
*
*       INT                                 vector_id if passed
*                                           ESAL_DP_INT_VECTOR_ID_DELIMITER
*                                           if failed
*
***********************************************************************/
INT     ESAL_AR_INT_Disable(INT vector_id)
{
    UINT sr_tmp;


    /* Read status register */
    ESAL_TS_RTE_CP_READ(0, &sr_tmp, ESAL_TS_RTE_CPR12, 0);

    /* Get vector_id register bit and
       clear vector_id register bit in status */
    sr_tmp &= ~(1UL << (vector_id + ESAL_AR_ISR_CR_IP0_BIT_POS));

    /* Write new status value */
    ESAL_TS_RTE_CP_WRITE(0, sr_tmp, ESAL_TS_RTE_CPR12, 0);
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SSNOP_EXECUTE();
    ESAL_TS_RTE_SYNC_EHB_EXECUTE();

    /* Return the vector ID */
    return (vector_id);

}

#if (ESAL_AR_OS_TIMER_USED == NU_TRUE)

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_AR_TMR_OS_Timer_Start
*
*   DESCRIPTION
*
*       This function starts the Operating System utilized timer.
*       This includes determining the required timer count for the
*       specified period, enabling the timer to produce this period,
*       and enabling the interrupt associated with this timer.
*
*   CALLED BY
*
*       ESAL_GE_TMR_OS_Timer_Start
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       ticks_per_sec                       Number of timer interrupts
*                                           that occur in a 1 sec period
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_AR_TMR_OS_Timer_Start(UINT32 ticks_per_sec)
{
    /* Calculate hardware timer count value to get required timer 
       ticks per second interrupt frequency.
       NOTE:    This calculation is based on the development platform
                clock rate and prescale value (see esal_dp_cfg.h). */
    ESAL_AR_TMR_OS_Count = ESAL_GE_TMR_COUNT_CALC(ESAL_PR_TMR_OS_CLOCK_RATE, 
                                                  ESAL_PR_TMR_OS_CLOCK_PRESCALE, 
                                                  ticks_per_sec);

    /* Enabling hardware interrupt 5 */
    ESAL_GE_INT_Enable(ESAL_AR_HW5_INT_VECTOR_ID, ESAL_TRIG_NOT_SUPPORTED,
                       ESAL_PRI_NOT_SUPPORTED);

    /* Performing end of interrupt.
       NOTE: This will reload the compare and count register in CP0. */
    ESAL_GE_TMR_OS_TIMER_EOI(ESAL_GE_TMR_OS_VECTOR);
}

#endif  /* ESAL_AR_OS_TIMER_USED */

#if (ESAL_CO_CACHE_AVAILABLE == NU_TRUE)

/***********************************************************************
*
*   FUNCTION
*
*       ESAL_CO_MEM_Cache_Enable
*
*   DESCRIPTION
*
*       This function initializes the cache as required
*       for the given core.  The memory region data structure
*       (ESAL_DP_MEM_Region_Data) should be utilized to perform
*       this initialization and the cache attributes in this
*       table should be correctly reflected.
*
*   CALLED BY
*
*       ESAL_GE_MEM_Initialize
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       avail_mem                           Address of available memory
*
*   OUTPUTS
*
*       VOID *                              Updated available memory
*                                           address
*
***********************************************************************/
VOID    *ESAL_CO_MEM_Cache_Enable(VOID *avail_mem)
{
    /* Return available memory */
    return (avail_mem);
}

#endif  /* ESAL_CO_CACHE_AVAILABLE */
