/***********************************************************************
*
*             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
*
*       ppc.c
*
*   DESCRIPTION
*
*       This file contains the PPC architecture specific functions
*
*   FUNCTIONS
*
*
*
*   DEPENDENCIES
*
*       nucleus.h
*
***********************************************************************/

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

/* External variable declarations */

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

/* Define the entry points for the Interrupt/Exception handlers. */
extern  VOID*               ESAL_AR_ISR_Critical;
extern  VOID*               ESAL_AR_ISR_Machine_Check;
extern  VOID*               ESAL_AR_ISR_DSI;
extern  VOID*               ESAL_AR_ISR_ISI;
extern  VOID*               ESAL_AR_ISR_External;
extern  VOID*               ESAL_AR_ISR_Align_Error;
extern  VOID*               ESAL_AR_ISR_Prog_Error;
extern  VOID*               ESAL_AR_ISR_FPU_Unavail;
extern  VOID*               ESAL_AR_ISR_System_Call;
extern  VOID*               ESAL_AR_ISR_Aux_Unavail;
extern  VOID*               ESAL_AR_ISR_Decrementer;
extern  VOID*               ESAL_AR_ISR_Fixed_Interval_Timer;
extern  VOID*               ESAL_AR_ISR_Watchdog_Timer;
extern  VOID*               ESAL_AR_ISR_Data_TLB;
extern  VOID*               ESAL_AR_ISR_Instr_TLB;
extern  VOID*               ESAL_AR_ISR_Debug;

/* Define external linker command file symbols */
extern  UINT32              _ld_bss_start;
extern  UINT32              _ld_bss_end;
extern  UINT32              _ld_ram_data_start;
extern  UINT32              _ld_ram_data_end;
extern  UINT32              _ld_rom_data_start;

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

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

    /* Set initial Count Register (CTR) */
    stack_frame -> min_stack.ctr = 0;

    /* Set initial Integer Exception Register (XER) */
    stack_frame -> min_stack.xer = 0;

    /* Set initial Condition Register (CR) */
    stack_frame -> min_stack.cr = 0;

    /* Set initial Link Register (LR) */
    stack_frame -> min_stack.lr = 0;

#if (ESAL_AR_STK_FPU_SUPPORT == NU_TRUE)

    /* Initialize the FPSCR register */
    ESAL_TS_RTE_FPSCR_READ(&(stack_frame -> fpscr));

#endif

    /* Set initial msr value (ensuring interrupts are enabled) */
    ESAL_TS_RTE_MSR_READ(&(stack_frame -> min_stack.msr));
    stack_frame->min_stack.msr |= ESAL_AR_INTERRUPTS_ENABLE_BITS;

#if ((CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1) && defined(CFG_NU_OS_SVCS_DBG_ENABLE))

    /* Disable debug exceptions in initial msr value. */
    stack_frame->min_stack.msr &= ~ESAL_AR_DBG_MSR_DE_BIT;

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1) */

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

    /* 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 *)((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 ((VOID *)&_ld_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 = (UINT32)&_ld_bss_end - (UINT32)&_ld_bss_start;

   /* Clear all BSS / Zero-initialized memory */
   ESAL_GE_MEM_Clear((VOID *)&_ld_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 ))

    UINT32 init_data_size;


    /* Check destination address (RAM data start) is not equal to
       source address (ROM data start). */
    if (&_ld_ram_data_start != &_ld_rom_data_start)
    {
        /* Calculate init data size */
        init_data_size = (UINT32)&_ld_ram_data_end - (UINT32)&_ld_ram_data_start;

        /* Copy initialized data from ROM to RAM */
        ESAL_GE_MEM_Copy((VOID *)&_ld_ram_data_start,
                         (VOID *)&_ld_rom_data_start,
                         init_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)
{
#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0)

    UINT32     msr_value;

    /* Get current msr value */
    ESAL_TS_RTE_MSR_READ(&msr_value);

    /* Set clear IP bit in MSR.
       NOTE: IP = 0 forces interrupts to be vectored starting at 0x000. */
    msr_value &= ~ESAL_AR_ISR_MSR_IP_BIT;

    /* Set new msr value */
    ESAL_TS_RTE_MSR_WRITE(msr_value);

#else /* CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1 */

    /* Interrupt servicing initialization not required */

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0) */
}

/***********************************************************************
*
*   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
*
*       None
*
*   INPUTS
*
*       None
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
VOID    ESAL_AR_ISR_Vector_Table_Install(VOID)
{
#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0)

    UINT32 vect_table_size;
    UINT32 vect_table_addr = (UINT32)&ESAL_AR_ISR_Vector_Table;

    /* Ensure vector table is at correct location */
    if (vect_table_addr != (UINT32)ESAL_AR_ISR_VECT_TABLE_ADDRESS)
    {
        /* Determine size of vector table */
        vect_table_size = (UINT32)&ESAL_AR_ISR_Vector_Table_End -
                          (UINT32)&ESAL_AR_ISR_Vector_Table;

        /* Copy vector table to correct location */
        ESAL_GE_MEM_Copy((VOID *)ESAL_AR_ISR_VECT_TABLE_ADDRESS,
                         (VOID *)vect_table_addr,
                         vect_table_size);

        /* Flush back vector table from D-Cache to RAM. */
        ESAL_GE_MEM_DCACHE_FLUSH_INVAL(ESAL_AR_ISR_VECT_TABLE_ADDRESS, vect_table_size);

        /* Invalidate vector table memory from I-Cache. */
        ESAL_GE_MEM_ICACHE_INVALIDATE(ESAL_AR_ISR_VECT_TABLE_ADDRESS, vect_table_size);
    }

#else /* CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1 */

    /* Initialize the  Interrupt Vector Prefix Register (IVPR) and Interrupt
       Vector Offset Registers (IVORx) here */
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVPR,     (UINT32) &ESAL_AR_ISR_Vector_Table);

#if(ESAL_CO_CRITICAL_INTS_AVAIL)
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR0,    (UINT32) &ESAL_AR_ISR_Critical);
#endif

    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR1,    (UINT32) &ESAL_AR_ISR_Machine_Check);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR2,    (UINT32) &ESAL_AR_ISR_DSI);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR3,    (UINT32) &ESAL_AR_ISR_ISI);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR4,    (UINT32) &ESAL_AR_ISR_External);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR5,    (UINT32) &ESAL_AR_ISR_Align_Error);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR6,    (UINT32) &ESAL_AR_ISR_Prog_Error);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR8,    (UINT32) &ESAL_AR_ISR_System_Call);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR10,   (UINT32) &ESAL_AR_ISR_Decrementer);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR11,   (UINT32) &ESAL_AR_ISR_Fixed_Interval_Timer);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR12,   (UINT32) &ESAL_AR_ISR_Watchdog_Timer);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR13,   (UINT32) &ESAL_AR_ISR_Data_TLB);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR14,   (UINT32) &ESAL_AR_ISR_Instr_TLB);
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR15,   (UINT32) &ESAL_AR_ISR_Debug);

#if (ESAL_AR_STK_FPU_SUPPORT == NU_TRUE)
    /* Floating point exception */
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR7,    (UINT32) &ESAL_AR_ISR_FPU_Unavail);
#endif

#if (ESAL_PR_AUXILIARY_PROC_AVAILABLE == NU_TRUE)
    ESAL_TS_RTE_SPR_WRITE(ESAL_TS_RTE_IVOR9,    (UINT32) &ESAL_AR_ISR_Aux_Unavail);
#endif

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0) */
}

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

    /* This is to prevent compiler warnings */
    NU_UNUSED_PARAM(trigger_type);
    NU_UNUSED_PARAM(priority);

    /* Read MSR register */
    ESAL_TS_RTE_MSR_READ(&msr_temp);

    /* Enable AR interrupt in MSR register */
    switch(vector_id)
    {
        case ESAL_AR_MACHINE_CHECK_INT_VECTOR_ID:

            /* Enable machine check interrupt */
            msr_temp |= ESAL_AR_INT_MACHINE_CHECK_BIT;

        break;

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)
        case ESAL_AR_FIT_INT_VECTOR_ID:
#endif
        case ESAL_AR_EXTERNAL_INT_VECTOR_ID:
        case ESAL_AR_DECREMENTER_INT_VECTOR_ID:

            /* Enable external and decrementer interrupt */
            msr_temp |= ESAL_AR_INT_EXTERNAL_DECREMENTER_BIT;

        break;

#if ESAL_CO_CRITICAL_INTS_AVAIL

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)
        case ESAL_AR_WATCHDOG_INT_VECTOR_ID:
#endif
        case ESAL_AR_CRITICAL_INT_VECTOR_ID:

            /* Enable critical interrupt */
            msr_temp |= ESAL_AR_INT_CRITICAL_BIT;

        break;

#endif /* ESAL_CO_CRITICAL_INTS_AVAIL */

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)

        case ESAL_AR_DEBUG_INT_VECTOR_ID:

            /* Enable debug interrupt */
            msr_temp |= ESAL_AR_INT_DEBUG_BIT;

        break;

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1) */

        default:

            /* Return error vector id */
            vector_id = ESAL_DP_INT_VECTOR_ID_DELIMITER;
    }

    /* Write MSR register */
    ESAL_TS_RTE_MSR_WRITE(msr_temp);

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

    /* Read MSR register */
    ESAL_TS_RTE_MSR_READ(&msr_temp);

    /* Disable AR interrupt in MSR register */
    switch(vector_id)
    {
        case ESAL_AR_MACHINE_CHECK_INT_VECTOR_ID:

            /* Disable machine check interrupt */
            msr_temp &= ~ESAL_AR_INT_MACHINE_CHECK_BIT;

        break;

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)
        case ESAL_AR_FIT_INT_VECTOR_ID:
#endif
        case ESAL_AR_EXTERNAL_INT_VECTOR_ID:
        case ESAL_AR_DECREMENTER_INT_VECTOR_ID:

            /* Disable external and decrementer interrupt */
            msr_temp &= ~ESAL_AR_INT_EXTERNAL_DECREMENTER_BIT;

        break;

#if ESAL_CO_CRITICAL_INTS_AVAIL

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)
        case ESAL_AR_WATCHDOG_INT_VECTOR_ID:
#endif
        case ESAL_AR_CRITICAL_INT_VECTOR_ID:

            /* Disable critical interrupt */
            msr_temp &= ~ESAL_AR_INT_CRITICAL_BIT;

        break;

#endif /* ESAL_CO_CRITICAL_INTS_AVAIL */

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1)

        case ESAL_AR_DEBUG_INT_VECTOR_ID:

            /* Disable debug interrupt */
            msr_temp &= ~ESAL_AR_INT_DEBUG_BIT;

        break;

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1) */

        default:

            /* Return error vector id */
            vector_id = ESAL_DP_INT_VECTOR_ID_DELIMITER;
    }

    /* Write MSR register */
    ESAL_TS_RTE_MSR_WRITE(msr_temp);

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

    /* Calculate hardware timer count value to get required timer
       ticks per second interrupt frequency. */
    timer_count = ESAL_GE_TMR_COUNT_CALC(ESAL_PR_TMR_OS_CLOCK_RATE,
                                         ESAL_PR_TMR_OS_CLOCK_PRESCALE,
                                         ticks_per_sec);

#if (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0)

    /* Save the timer count into the data structure associated with the
       timer interrupt vector ID */
    ESAL_GE_EXCEPT_VECTOR_DATA_SET(ESAL_GE_TMR_OS_VECTOR, (VOID *)timer_count);

#else /* CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 1 */

    /* Load Decrementer Auto-reload Register (DECAR) and the Decrementer (DEC)
       with timer count */
    ESAL_TS_RTE_SPR_WRITE(ESAL_AR_TMR_DECAR_REG, timer_count);
    ESAL_TS_RTE_SPR_WRITE(ESAL_AR_TMR_DEC_REG, timer_count);

#endif /* (CFG_NU_OS_ARCH_PPC_COM_PPC_VARIANT == 0) */

    /* Perform a timer EOI / clear sequence.  This will force the
       decrementer to be reloaded with the count value calculated above. */
    ESAL_GE_TMR_OS_TIMER_EOI(ESAL_GE_TMR_OS_VECTOR);
}

#endif  /* ESAL_AR_OS_TIMER_USED == NU_TRUE */

