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

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

/* External variable declarations */
extern  VOID        * _bss_start;
extern  VOID        * _bss_end;
extern  VOID        * _rom_store_table;

/* Declare architecture interrupt stacks */

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

#if (XCHAL_CP_NUM > 0)

    INT *save_area;
    INT *cp_enable;
    NU_MEMORY_POOL *sys_pool_ptr;

    /* Reserve start of the stack to be used for coprocessor save area pointer and cpenable.
     * NOTE : subtracted 4 UINT32 to keep stack 16-byte aligned */
    stack_frame = (ESAL_AR_STK *)((VOID_CAST)end_addr - ESAL_GE_STK_MAX_FRAME_SIZE - (sizeof(UINT32) * 4));
    
    /* Clear stack frame. */
    ESAL_GE_MEM_Clear(stack_frame,ESAL_GE_STK_MAX_FRAME_SIZE);

    save_area = (INT *)(end_addr - (sizeof(UINT32) * 2));
    
    /* Allocate 16-byte aligned memory for the save area */
    (VOID)NU_System_Memory_Get(&sys_pool_ptr, NU_NULL);
    (VOID)NU_Allocate_Aligned_Memory(sys_pool_ptr, (VOID*)save_area, (UNSIGNED)ESAL_AR_CP_SIZE, 16, NU_SUSPEND);
    
    /* Get the actual save area pointer to initialize */
    save_area = (INT *)(*save_area);
    
    /* Initialize the save area */
    memset(save_area, 0, ((UINT32)ESAL_AR_CP_SIZE));
    
    /* Get the cpenable address */
    cp_enable = (INT *)(end_addr - sizeof(UINT32));
    
    /* Initialize CP Enable */
    memset(cp_enable, 0, (sizeof(UINT32)));

#else

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

    /* Clear stack frame. */
    ESAL_GE_MEM_Clear(stack_frame,ESAL_GE_STK_MAX_FRAME_SIZE);

#endif

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

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

    /* Set program counter entry point */
    stack_frame -> min_stack.rtn_address = (UINT32)entry_function;

    /* Set initial PS value with CALLINC 1 to behave as call4
       and handle next window spill/overflow smoothly. */
    stack_frame -> min_stack.reg_ps = 0x50030;

    /* Set stack frame pointer. */
    stack_frame -> min_stack.a1 =(UINT32) stack_frame + ESAL_GE_STK_MAX_FRAME_SIZE;

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

    UINT return_address;

    /*
     *
     * Here we build a stack frame that looks as if it had been constructed
     * by the above solicited context-switch code.  It is built such that
     * on context restore, ie. after return via RETW, PC points to the
     * function TCCT_HISR_Shell, as if code immediately preceding
     * that label had done a call4 to context-saving code as above.
     * To do so, we build a stack that looks like the following:
     *
     *           -16    saved a0 of TCCT_HISR_Shell (0)
     *           -12    saved sp of TCCT_HISR_Shell (Saved sp + ESAL_GE_STK_MIN_FRAME_SIZE+16)
     *            -8    saved a2 of TCCT_HISR_Shell (undefined)
     *            -4    saved a3 of TCCT_HISR_Shell (undefined)
     * Saved sp   +0 -> ESAL_GE_STK_TS_TYPE = 0
     *            +4    ESAL_TS_STK_OFFSET_A0 = TCCT_HISR_Shell (upper bits adjusted)
     *             :
     * +ESAL_GE_STK_MIN_FRAME_SIZE+0    (unused save area, TCCT_HISR_Shell never returns)
     * +ESAL_GE_STK_MIN_FRAME_SIZE+16 = sp of TCCT_HISR_Shell
     */

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

#if XCHAL_HAVE_WINDOWED
    /* Set address of stack frame. */
    stack_frame = (ESAL_TS_STK *)((UINT)end_addr - ESAL_GE_STK_MIN_FRAME_SIZE - 
                                  ESAL_TS_STK_CALL4_HEADER_SIZE - ESAL_TS_STK_SAVE_AREA_SIZE);

    /* Calculate base save area address. */
    call4_header = (UINT32 *) ((UINT32) stack_frame - ESAL_TS_STK_SAVE_AREA_SIZE);

    /* Set base save area. */
    call4_header[0] = 0;
    call4_header[1] = (UINT32)end_addr;
    call4_header[2] = 0;
    call4_header[3] = 0;
#else
    /* Set address of stack frame. */
    stack_frame = (ESAL_TS_STK *)((UINT)end_addr - ESAL_GE_STK_MIN_FRAME_SIZE);
#endif

    /* Clear stack frame. */
    ESAL_GE_MEM_Clear(stack_frame, ESAL_GE_STK_MIN_FRAME_SIZE);

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

    /* Store entry function address in temporary variable for further processing*/
    return_address = (UINT)entry_function;

    /* Mask off call window increment and make return address look like call4 */
    return_address = (0x3fffffff & return_address) | 0x40000000;

    /* Set return address / entry point within stack frame */
    stack_frame -> rtn_address = return_address;

    /* 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 first available memory to caller */
    return ((VOID *)&_bss_end);
}


/***********************************************************************
*
*   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)
{
    UINT32    size;

    /* Calculate the sizee of bss */
    size = (UINT32)&_bss_end - (UINT32)&_bss_start;

    /* Clear all BSS / Zero-initialized memory
       NOTE:   This function will most likely use linker produced symbols
               or a toolset library function to perform this operation. */

    ESAL_GE_MEM_Clear((VOID *)&_bss_start, 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 ))

    NU_REG UINT32 rom_store_table = (UINT32)&_rom_store_table;

    UINT32  start_rom_addr  = rom_store_table;
    UINT32  end_rom_addr    = rom_store_table + 4;
    UINT32  ram_store_addr  = rom_store_table + 8;

    UINT data_length;

    /* Check if there is something to be copied from ROM to RAM */
    if (rom_store_table != 0)
    {
        /* Get length to copy */
        data_length = (UINT)((UINT)end_rom_addr - (UINT)start_rom_addr);

        /* Copy initialized data from ROM to RAM */
        ESAL_GE_MEM_Copy((VOID *)ram_store_addr, (VOID *)start_rom_addr, data_length);
    }

#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)
{
}


/***********************************************************************
*
*   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)
{
    /* Vector table install not required */
}


/***********************************************************************
*
*   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)
{
    /* No architecture interrupts */
    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)
{
    /* No architecture interrupts */
    return (vector_id);
}

