/***********************************************************************
*
*             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_mmu.c
*
*   DESCRIPTION
*
*       This file contains the ARM architecture MMU functions
*
*   FUNCTIONS
*
*       ESAL_CO_MEM_Cache_Enable
*       ESAL_CO_MEM_Region_Setup
*
*   DEPENDENCIES
*
*       esal.h                              Embedded Software
*                                           Abstraction Layer external
*                                           interface
*
***********************************************************************/

/* Include required header files */
#include            "nucleus.h"
#include            "os/kernel/plus/core/inc/esal.h"

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT != 2)

/* System Error Handler function prototype. */
extern VOID    ERC_System_Error(INT error_code);

/* Local Function prototypes */
static VOID    ESAL_CO_MEM_Region_Setup(INT region_num,
                                        UINT32 vrt_addr,
                                        UINT32 phy_addr,
                                        UINT32 size,
                                        ESAL_GE_CACHE_TYPE cache_type,
                                        UINT32 access_type);


/***********************************************************************
*
*   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
*
*       ESAL_CO_MEM_Region_Setup
*
*   INPUTS
*
*       avail_mem                           Address of available memory
*
*   OUTPUTS
*
*       VOID *                              Updated available memory
*                                           address
*
***********************************************************************/
VOID    *ESAL_CO_MEM_Cache_Enable(VOID *avail_mem)
{
    INT                 region_num;
    UINT32              cp15_ctrl_val;
    VOID *              tlb_mem;

    /* Read current CP15 control register value */
    ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &cp15_ctrl_val, ESAL_TS_RTE_C1, ESAL_TS_RTE_C0, 0);

    /* Clear the V bit(13) to set Normal exception vectors range. */
    cp15_ctrl_val &= ~(ESAL_CO_MEM_CP15_CTRL_V);

    /* Clear the alignment bit(1) to enable unaligned memory accesses*/
    cp15_ctrl_val &= ~(ESAL_CO_MEM_CP15_CTRL_A);

    /* Write updated CP15 control register value */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, cp15_ctrl_val, ESAL_TS_RTE_C1, ESAL_TS_RTE_C0, 0);
    ESAL_TS_RTE_NOP_EXECUTE();
    ESAL_TS_RTE_NOP_EXECUTE();
    ESAL_TS_RTE_NOP_EXECUTE();

    /* Find the start of TLB memory in the target memory table.  Use the memory
       in the first entry in the table to enable the search. */
    tlb_mem = ESAL_GE_MEM_Next_Match_Find((VOID *)ESAL_DP_MEM_Region_Data[0].physical_start_addr,
                                          ESAL_NOCACHE,
                                          ESAL_TLB_MEM,
                                          ESAL_DATA);

    if (tlb_mem == (VOID *)ESAL_GE_MEM_ERROR)
    {
        /* ERROR: Unable to find TLB memory region in target memory table. */
        ERC_System_Error(NU_NULL);
    }

    /* Check alignment of available memory pointer */
    if (!(ESAL_GE_MEM_ALIGNED_CHECK(tlb_mem,ESAL_CO_MEM_TTB_SIZE)))
    {
        /* Align the pointer to the required boundary */
        avail_mem = ESAL_GE_MEM_PTR_ALIGN(tlb_mem,ESAL_CO_MEM_TTB_SIZE);
    }

    /* Clear the entire translation table */
    ESAL_GE_MEM_Clear(tlb_mem, ESAL_CO_MEM_TTB_SIZE);

    /* Set translation table base address */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, tlb_mem, ESAL_TS_RTE_C2, ESAL_TS_RTE_C0, 0);

    /* Walk through all development platform memory regions defined in table */
    for (region_num = 0; region_num < ESAL_DP_MEM_NUM_REGIONS; region_num++)
    {
        /* Enable caches for this memory section */
        ESAL_CO_MEM_Region_Setup(region_num,
                                 (UINT32)ESAL_DP_MEM_Region_Data[region_num].virtual_start_addr,
                                 (UINT32)ESAL_DP_MEM_Region_Data[region_num].physical_start_addr,
                                 ESAL_DP_MEM_Region_Data[region_num].size,
                                 ESAL_DP_MEM_Region_Data[region_num].cache_type,
                                 ESAL_DP_MEM_Region_Data[region_num].access_type);

    }   /* for loop */

    /* Set the domain access for domain D0 */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, ESAL_CO_MEM_DOMAIN_D0_MANAGER_ACCESS,
                         ESAL_TS_RTE_C3, ESAL_TS_RTE_C0, 0);

    /* Invalidate all TLB entries before enabling the MMU */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, 0, ESAL_TS_RTE_C8, ESAL_TS_RTE_C7, 0);

    /* Read current CP15 control register value */
    ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &cp15_ctrl_val, ESAL_TS_RTE_C1, ESAL_TS_RTE_C0, 0);

    /* Set instruction cache enable / data cache enable / MMU enable bits */
    cp15_ctrl_val |= (ESAL_CO_MEM_CP15_CTRL_I | ESAL_CO_MEM_CP15_CTRL_C | ESAL_CO_MEM_CP15_CTRL_M);

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)

    /* Enable branch prediction */
    cp15_ctrl_val |=  ESAL_CO_MEM_CP15_CTRL_Z;

#endif  /* CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1 */

    /* Write updated CP15 control register value */
    ESAL_TS_RTE_CP_WRITE(ESAL_TS_RTE_CP15, 0, cp15_ctrl_val, ESAL_TS_RTE_C1, ESAL_TS_RTE_C0, 0);
    ESAL_TS_RTE_NOP_EXECUTE();
    ESAL_TS_RTE_NOP_EXECUTE();
    ESAL_TS_RTE_NOP_EXECUTE();

    /* Return updated available memory address to caller. */
    return (avail_mem);
}


/***********************************************************************
*
*   FUNCTION
*
*       ESAL_CO_MEM_Region_Setup
*
*   DESCRIPTION
*
*       This function sets-up the region of memory based on the given
*       attributes
*
*   CALLED BY
*
*       ESAL_CO_MEM_Cache_Enable
*
*   CALLS
*
*       None
*
*   INPUTS
*
*       region_num                          Number of region begin setup
*       vrt_addr                            Virtual address of region
*       phy_addr                            Physical address of region
*       size                                Size of region
*       cache_type                          Cache type of region
*       access_type                         Memory access type of region
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
static VOID    ESAL_CO_MEM_Region_Setup(INT region_num,
                                        UINT32 vrt_addr,
                                        UINT32 phy_addr,
                                        UINT32 size,
                                        ESAL_GE_CACHE_TYPE cache_type,
                                        UINT32 access_type)
{
    UINT32      section_offset;
    UINT32      ttb_offset;
    UINT32      ttb_value;
    UINT32      ttb_base;


    /* Access unused parameters to avoid toolset warnings */
    NU_UNUSED_PARAM(access_type);
    NU_UNUSED_PARAM(region_num);

    /* Read ttb base address */
    ESAL_TS_RTE_CP_READ(ESAL_TS_RTE_CP15, 0, &ttb_base, ESAL_TS_RTE_C2, ESAL_TS_RTE_C0, 0);

    /* Ensure the virtual and physical addresses are aligned on a
       section boundary */
    vrt_addr &= ESAL_CO_MEM_TTB_SECT_SIZE_MASK;
    phy_addr &= ESAL_CO_MEM_TTB_SECT_SIZE_MASK;

    /* Loop through entire region of memory (one MMU section at a time).
       Each section requires a TTB entry. */
    for (section_offset = 0; section_offset < size; section_offset += ESAL_CO_MEM_TTB_SECT_SIZE)
    {

        /* Calculate translation table entry offset for this memory section */
        ttb_offset = ((vrt_addr + section_offset) >> ESAL_CO_MEM_TTB_SECT_TO_DESC_SHIFT);

        /* Build translation table entry value */
        ttb_value = (phy_addr + section_offset) | ESAL_CO_MEM_TTB_DESC_ALL_ACCESS;

#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
        /* Check if memory region type is Memory mapped. */
        if (ESAL_DP_MEM_Region_Data[region_num].mem_type != ESAL_MEM_MAPPED)
        {
#endif  /* CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1 */

            /* Set cache related bits in translation table entry.
               NOTE: Default is uncached instruction and data. */
            if (cache_type == ESAL_WRITEBACK)
            {
                /* Update translation table entry value */
                ttb_value |= (ESAL_CO_MEM_TTB_DESC_B | ESAL_CO_MEM_TTB_DESC_C);
            }
            else if (cache_type == ESAL_WRITETHROUGH)
            {
                /* Update translation table entry value */
                ttb_value |= ESAL_CO_MEM_TTB_DESC_C;
            }
#if (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1)
            /* In case of un-cached memory, set TEX 0 bit to set memory
               attribute to normal. */
            else if (cache_type == ESAL_NOCACHE)
            {
                ttb_value |= ESAL_CO_MEM_TTB_DESC_TEX;
            }
        }
#endif  /* CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT == 1 */

        /* Write translation table entry value to entry address */
        ESAL_GE_MEM_WRITE32(ttb_base + ttb_offset, ttb_value);

    }   /* for loop */
}

#endif /* (CFG_NU_OS_ARCH_ARM_COM_MMU_VARIANT != 2) */
