/***********************************************************************
*
*             Copyright 2006 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
*
*       arm.c
*
*   DESCRIPTION
*
*       This file contains the core ARM architecture functions
*
*   FUNCTIONS
*
*
*
*   DEPENDENCIES
*
*       nucleus.h
*
***********************************************************************/

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

/* Define architecture mask and ARVv7 bits */
#define MIDR_ARCH_MASK              0x000F0000    /* Main ID register's architecture mask */
#define MIDR_ARCH_ARMV7             0xF           /* ARMv7 */
#define MIDR_PART_NO_CORTEX_A       0xC00         /* Primary part number of Cortex-A series. */
#define MIDR_PART_NO_MASK           0x0000FF00    /* Primary part number mask  */

/* External variable declarations */
extern  VOID                            *ESAL_AR_ISR_Vector_Table[ESAL_AR_ISR_VECTOR_TABLE_SIZE/sizeof(VOID *)];

/* Declare architecture interrupt stacks */
static UINT32                           ESAL_AR_ISR_IRQ_Data[ESAL_AR_ISR_STACK_SIZE];
static UINT32                           ESAL_AR_ISR_FIQ_Data[ESAL_AR_ISR_STACK_SIZE];
static UINT8                            ESAL_AR_ISR_SUP_Stack[ESAL_AR_ISR_SUP_STACK_SIZE];


/***********************************************************************
*
*   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;


    /* Ensure unused parameter doesn't cause tool warnings */
    NU_UNUSED_PARAM(start_addr);

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

    /* Check if entry function is executing in THUMB mode */
    if ((UINT32)entry_function & ESAL_AR_STK_THUMB_MASK)
    {
        /* Write initial SPSR value for THUMB execution */
        (stack_frame -> min_stack).spsr = (ESAL_AR_INT_CPSR_THUMB | ESAL_AR_INT_CPSR_SYS_MODE);
    }
    else
    {
        /* Set initial SPSR value for ARM execution */
        (stack_frame -> min_stack).spsr = ESAL_AR_INT_CPSR_SYS_MODE;
    }

    if(ESAL_PR_ENDIANESS == ESAL_BIG_ENDIAN)
    {
        /* Set initial endianess value within stack frame */
        (stack_frame -> min_stack).spsr |= ESAL_AR_INT_CPSR_E_BIT;
    }
    /* Set stack type within stack frame */
    stack_frame -> stack_type = ESAL_GE_STK_AR_TYPE;

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

    /* Zeroize lr within stack frame (allows nicer debugging environment - call stack terminated) */
    (stack_frame -> min_stack).lr = 0x00000000;

#if (ESAL_TS_PIC_PID_SUPPORT == NU_TRUE)

    /* Ensure PIC / PID base address is set in stack frame */
    ESAL_TS_RTE_PIC_PID_BASE_GET(NU_NULL, &(stack_frame -> r9));

#endif  /* ESAL_TS_PIC_PID_SUPPORT == NU_TRUE */

#if (CFG_NU_OS_ARCH_ARM_COM_FPU_SUPPORT > 0)

    /* Set FPSCR to 0 */
    stack_frame -> fpscr = 0;

#endif  /* CFG_NU_OS_ARCH_ARM_COM_FPU_SUPPORT > 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;


    /* Ensure unused parameter doesn't cause tool warnings */
    NU_UNUSED_PARAM(start_addr);

    /* Set address of stack frame */
    stack_frame = (ESAL_TS_STK *)((UINT32)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 = (UINT32)entry_function;

#if (ESAL_TS_PIC_PID_SUPPORT == NU_TRUE)

    /* Ensure PIC / PID base address is set in stack frame */
    ESAL_TS_RTE_PIC_PID_BASE_GET(NU_NULL, &(stack_frame -> r9));

#endif  /* ESAL_TS_PIC_PID_SUPPORT == NU_TRUE */

#if (CFG_NU_OS_ARCH_ARM_COM_FPU_SUPPORT > 0)

    /* Set FPSCR to 0 */
    stack_frame -> fpscr = 0x03000000;

#endif  /* CFG_NU_OS_ARCH_ARM_COM_FPU_SUPPORT > 0 */

    /* 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
}


/***********************************************************************
*
*   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)
{
    /* Switch to IRQ mode (keeping interrupts disabled) */
    ESAL_TS_RTE_CPSR_C_WRITE(ESAL_AR_INT_CPSR_IRQ_MODE | ESAL_AR_INTERRUPTS_DISABLE_BITS);

    /* Set IRQ stack pointer */
    ESAL_TS_RTE_SP_WRITE((VOID *)&ESAL_AR_ISR_IRQ_Data);

    /* Switch to FIQ mode (keeping interrupts disabled) */
    ESAL_TS_RTE_CPSR_C_WRITE(ESAL_AR_INT_CPSR_FIQ_MODE | ESAL_AR_INTERRUPTS_DISABLE_BITS);

    /* Set FIQ stack pointer */
    ESAL_TS_RTE_SP_WRITE((VOID *)&ESAL_AR_ISR_FIQ_Data);

    /* Switch to Supervisor mode (keeping interrupts disabled) */
    ESAL_TS_RTE_CPSR_C_WRITE(ESAL_AR_INT_CPSR_SUP_MODE | ESAL_AR_INTERRUPTS_DISABLE_BITS);

    /* Set Supervisor stack pointer */
    ESAL_TS_RTE_SP_WRITE(ESAL_GE_STK_ALIGN(&ESAL_AR_ISR_SUP_Stack[ESAL_AR_ISR_SUP_STACK_SIZE-1]));

    /* Switch back to System mode (keeping interrupts disabled) */
    ESAL_TS_RTE_CPSR_C_WRITE(ESAL_AR_INT_CPSR_SYS_DISABLED);
}


/***********************************************************************
*
*   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)
{
    UINT32  arch = 0;
    VOID    *dst_addr = (VOID *)ESAL_AR_ISR_VECTOR_TABLE_DEST_ADDR_ROM;


#if (CFG_NU_OS_KERN_PLUS_CORE_ROM_SUPPORT == NU_FALSE)

    /* Assign destination address of vector table to RAM address */
    dst_addr = (VOID *)ESAL_PR_ISR_VECTOR_TABLE_DEST_ADDR_RAM;

#endif  /* CFG_NU_OS_KERN_PLUS_CORE_ROM_SUPPORT == NU_FALSE */

    /* Read Main ID Register (MIRD) */
    ESAL_TS_RTE_CP_READ(p15, 0, &arch, c0, c0, 0);

/* QEMU has bug where writting VBAR crashes emulator */
#ifndef CFG_NU_BSP_REALVIEW_EB_CT926EJS_ENABLE
    /* Check if Cortex-A series of ARMv7 architecture. */
    if (((arch & MIDR_ARCH_MASK) >> 16) == MIDR_ARCH_ARMV7 && ((arch & MIDR_PART_NO_MASK) >> 4) == MIDR_PART_NO_CORTEX_A)
    {
        /* Set vector base address */
        ESAL_TS_RTE_CP_WRITE(p15, 0, dst_addr, c12, c0, 0);
        ESAL_TS_RTE_NOP_EXECUTE();
        ESAL_TS_RTE_NOP_EXECUTE();
        ESAL_TS_RTE_NOP_EXECUTE();
    }
#endif  /* !CFG_NU_BSP_REALVIEW_EB_CT926EJS_ENABLE */

    /* Check if the hardware vector table is located at the required location */
    if (&ESAL_AR_ISR_Vector_Table != dst_addr)
    {
        /* Copy vector table from linked position to the processor
           specified location */
        ESAL_GE_MEM_Copy(dst_addr,ESAL_AR_ISR_Vector_Table,ESAL_AR_ISR_VECTOR_TABLE_SIZE);

#ifdef ESAL_CO_XSCALE_CORE

        /* From the Multi-ICE 2.2 manual:

           "The debug version of the reset vector is held in a cache line in a part of the cache
           called the mini-ICache. The mini-ICache is mapped over the memory starting at 0x0 and
           at 0xffff0000 and is used when the processor is in debug state.

           Cache lines on current XScale processors are big enough to include every exception
           vector, not just the reset vector. This means that when a debugger is active, other
           exceptions, for example interrupts, also use the debug vectors, and might therefore fail."

           Mentor Graphics debuggers use the following instruction as a cue to update the
           mini-ICache, eliminating the need to halt the processor after we write the vector
           table (above) when using various debug connections. */
        ESAL_TS_RTE_BRK_EXECUTE(ESAL_AR_ISR_XSCALE_VECT_INSTALL_BRK);

#endif /* ESAL_CO_XSCALE_CORE */

    }
}


/***********************************************************************
*
*   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)
{
    UINT32      temp32=0;


    /* Access unused parameters to avoid compiler warnings */
    NU_UNUSED_PARAM(trigger_type);
    NU_UNUSED_PARAM(priority);

    /* Get the current CPSR interrupt bits */
    ESAL_AR_INT_BITS_GET(&temp32);

    /* Check if enabling IRQ interrupt */
    if (vector_id == ESAL_AR_IRQ_INT_VECTOR_ID)
    {
        /* Clear the IRQ bit */
        temp32 &= ~ESAL_AR_INT_CPSR_IRQ_BIT;
    }
    else
    {
        /* Clear the FIQ bit */
        temp32 &= ~ESAL_AR_INT_CPSR_FIQ_BIT;
    }

    /* Set the new interrupt bits in the CPSR */
    ESAL_AR_INT_BITS_SET(temp32);

    /* 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)
{
    UINT32      temp32=0;


    /* Get the current CPSR interrupt bits */
    ESAL_AR_INT_BITS_GET(&temp32);

    /* Check if disabling IRQ interrupt */
    if (vector_id == ESAL_AR_IRQ_INT_VECTOR_ID)
    {
        /* Set the IRQ bit */
        temp32 |= ESAL_AR_INT_CPSR_IRQ_BIT;
    }
    else
    {
        /* Set the FIQ bit */
        temp32 |= ESAL_AR_INT_CPSR_FIQ_BIT;
    }

    /* Set the new interrupt bits in the CPSR */
    ESAL_AR_INT_BITS_SET(temp32);

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