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

*
***********************************************************************/

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

#if (CFG_NU_OS_ARCH_PPC_COM_MMU_VARIANT == PPC_MMU_E300_VARIANT)

#include      "arch/ppc/ppc_mmu_e300_defs.h"

/* Local Function prototypes */
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,
                                 UINT8 *ibat_num,
                                 UINT8 *dbat_num);


/***********************************************************************
*
*   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)
{
    UINT32  temp32;
    INT     count;
    UINT32  hid0_E, hid0_I;
    UINT32  hid2_E;
    UINT8   ibat_num = 0;
    UINT8   dbat_num = 0;

    /* Get current HID2 register value */
    ESAL_TS_RTE_SYNC_EXECUTE();
    ESAL_TS_RTE_SPR_READ(ESAL_CO_MEM_HID2_REG, &hid2_E);

    /* Set High BAT Enable bit */
    hid2_E |= ESAL_CO_MEM_HID2_HBE_ENABLE;

    /* Set new HID2 register value */
    ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_HID2_REG, hid2_E);
    ESAL_TS_RTE_SYNC_EXECUTE();

    /* Disable instruction and data cache */
    ESAL_CO_MEM_CACHE_DISABLE();

    /********************************/
    /* BAT INITIALIZE               */
    /********************************/

    /* Loop through all sections in the memory description table */
    for (count = 0; count < ESAL_DP_MEM_NUM_REGIONS; count++)
    {
        /* Enable caches for this memory section */
        ESAL_CO_MEM_Region_Setup(count,
                                 (UINT32)ESAL_DP_MEM_Region_Data[count].virtual_start_addr,
                                 (UINT32)ESAL_DP_MEM_Region_Data[count].physical_start_addr,
                                 ESAL_DP_MEM_Region_Data[count].size,
                                 ESAL_DP_MEM_Region_Data[count].cache_type,
                                 ESAL_DP_MEM_Region_Data[count].access_type,
                                 &ibat_num,
                                 &dbat_num);
    } /* for loop */

    /*************************************/
    /* ENABLE INSTRUCTION AND DATA CACHE */
    /*************************************/

    /* Get current MSR value */
    ESAL_TS_RTE_MSR_READ(&temp32);

    /* Enable instruction (IR) and data (DR) address translation */
    temp32 |= (ESAL_CO_MEM_MSR_IR_BIT | ESAL_CO_MEM_MSR_DR_BIT);

    /* Set new MSR value */
    ESAL_TS_RTE_MSR_WRITE(temp32);
    ESAL_TS_RTE_ISYNC_EXECUTE();

    /********************************/
    /* ENABLE CACHE                 */
    /********************************/

    /* Set HID0 to enable instruction cache */

    ESAL_TS_RTE_SPR_READ(ESAL_CO_MEM_HID0_REG, &hid0_E);

    /* Create the Instruction Cache Enable value */
    hid0_E |= (ESAL_CO_MEM_HID0_ICACHE_ENABLE);

    /* Create the Instruction Cache Enable and Invalidate value */
    hid0_I = (hid0_E | ESAL_CO_MEM_HID0_ICACHE_INVALIDATE);

    /* Sync data and instruction cache */
    ESAL_TS_RTE_SYNC_EXECUTE();
    ESAL_TS_RTE_ISYNC_EXECUTE();

    /* Enable and Invalidate the Instruction cache */
    ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_HID0_REG, hid0_I);

    /* Sync the Instruction cache */
    ESAL_TS_RTE_ISYNC_EXECUTE();

    /* Turn off the Cache Invalidate bit */
    ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_HID0_REG, hid0_E);

    /* Sync the Instruction cache */
    ESAL_TS_RTE_ISYNC_EXECUTE();

    /* Set HID0 to enable data cache */

    ESAL_TS_RTE_SPR_READ(ESAL_CO_MEM_HID0_REG, &hid0_E);

    ESAL_TS_RTE_SYNC_EXECUTE();
    hid0_E |= (ESAL_CO_MEM_HID0_DCACHE_ENABLE | ESAL_CO_MEM_HID0_DCACHE_INVALIDATE);
    ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_HID0_REG, hid0_E);
    ESAL_TS_RTE_ISYNC_EXECUTE();

    ESAL_TS_RTE_SYNC_EXECUTE();
    hid0_E &= ~(ESAL_CO_MEM_HID0_DCACHE_INVALIDATE);
    ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_HID0_REG, hid0_E);
    ESAL_TS_RTE_ISYNC_EXECUTE();

    /* Return updated available memory address */
    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
*       ibat_num                            Instruction BAT register
*       dbat_num                            Data BAT register
*
*   OUTPUTS
*
*       None
*
***********************************************************************/
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,
                                 UINT8 *ibat_num,
                                 UINT8 *dbat_num)

{
    UINT32  temp32;
    UINT32  upper_bat;
    UINT32  lower_bat, i_lower_bat;
    UINT8   ibat = *ibat_num;
    UINT8   dbat = *dbat_num;

    /* Initialize upper and lower bat values */
    lower_bat = ESAL_CO_MEM_LBAT_INIT;
    upper_bat = ESAL_CO_MEM_UBAT_INIT;

    /* Put physical address in bat low */
    lower_bat |= phy_addr;

    /* Put virtual address in bat high */
    upper_bat |= vrt_addr;

    /* Translate size into correct bits */
    temp32 = size;

    /* Check if block size if more than 128K. */
    if (temp32 > ESAL_GE_MEM_128K)
    {
        temp32 = (temp32 / ESAL_GE_MEM_128K) - 1;
        temp32 <<= ESAL_CO_MEM_UBAT_SIZE_OFFSET;
    }
    else
    {
        temp32 = 0;
    }

    /* OR size bits into upper bat */
    upper_bat |= temp32;

    /* Set appropriate bits based on cache type */
    if (cache_type == ESAL_NOCACHE)
    {
       /* Set appropriate bits in the lower bat register */
       lower_bat |= ESAL_CO_MEM_LBAT_NO_CACHE;
    }
    else if (cache_type == ESAL_WRITETHROUGH)
    {
       /* Set appropriate bits in the lower bat register */
       lower_bat |= ESAL_CO_MEM_LBAT_WT_CACHE_ENABLE;

    }
    else
    {
       /* Set appropriate bits in the lower bat register */
       lower_bat &= ~(ESAL_CO_MEM_LBAT_NO_CACHE | ESAL_CO_MEM_LBAT_WT_CACHE_ENABLE);

    }

    /* Check if memory region is memory mapped or I/O mapped */
    if ( (ESAL_DP_MEM_Region_Data[region_num].mem_type == ESAL_MEM_MAPPED) ||
        (ESAL_DP_MEM_Region_Data[region_num].mem_type == ESAL_IO_MAPPED) )
    {
       /* Set guarded bit in lower bat register */
       lower_bat |= ESAL_CO_MEM_LBAT_GUARDED_MEM_ENABLE;
    }

    /* Check if region is instruction region */
    if ( (access_type == ESAL_INST) ||
        (access_type == ESAL_INST_AND_DATA) )
    {

       /* W and G bits not allowed in I-BAT's */
       i_lower_bat = lower_bat & ~(ESAL_CO_MEM_LBAT_IBAT_MASK_BITS);

       /* Determine if this is a Read-Only memory region */
       if (ESAL_DP_MEM_Region_Data[region_num].mem_type == ESAL_ROM)
       {
           i_lower_bat |= ESAL_CO_MEM_LBAT_READ_ONLY;
       }

       /* Execute based on which instruction bat is being used */
       switch (ibat)
       {
           case 0:

               /* Write instruction upper bat 0 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT0U_REG, upper_bat);

               /* Write instruction lower bat 0 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT0L_REG, i_lower_bat);

           break;

           case 1:

               /* Write instruction upper bat 1 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT1U_REG, upper_bat);

               /* Write instruction lower bat 1 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT1L_REG, i_lower_bat);

           break;

           case 2:

               /* Write instruction upper bat 2 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT2U_REG, upper_bat);

               /* Write instruction lower bat 2 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT2L_REG, i_lower_bat);

           break;

           case 3:

               /* Write instruction upper bat 3 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT3U_REG, upper_bat);

               /* Write instruction lower bat 3 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT3L_REG, i_lower_bat);

           break;

           case 4:

               /* Write instruction upper bat 4 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT4U_REG, upper_bat);

               /* Write instruction lower bat 4 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT4L_REG, i_lower_bat);

           break;

           case 5:

               /* Write instruction upper bat 5 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT5U_REG, upper_bat);

               /* Write instruction lower bat 5 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT5L_REG, i_lower_bat);

           break;

           case 6:

               /* Write instruction upper bat 6 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT6U_REG, upper_bat);

               /* Write instruction lower bat 6 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT6L_REG, i_lower_bat);

           break;

           case 7:

               /* Write instruction upper bat 7 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT7U_REG, upper_bat);

               /* Write instruction lower bat 7 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_IBAT7L_REG, i_lower_bat);

           break;

           default:
           break;

       }   /* switch */

       /* Increment count of data bats used */
       ibat++;

    }   /* If instruction access */

    /* Check if region is data region */
    if ( (access_type == ESAL_DATA) ||
        (access_type == ESAL_INST_AND_DATA) )
    {

       /* Determine if this is a Read-Only memory region */
       if (ESAL_DP_MEM_Region_Data[region_num].mem_type == ESAL_ROM)
       {
           lower_bat |= ESAL_CO_MEM_LBAT_READ_ONLY;
       }

       /* Execute based on which data bat is being used */
       switch (dbat)
       {
           case 0:

               /* Write data upper bat 0 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT0U_REG, upper_bat);

               /* Write data lower bat 0 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT0L_REG, lower_bat);

           break;

           case 1:

               /* Write data upper bat 1 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT1U_REG, upper_bat);

               /* Write data lower bat 1 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT1L_REG, lower_bat);

           break;

           case 2:

               /* Write data upper bat 2 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT2U_REG, upper_bat);

               /* Write data lower bat 2 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT2L_REG, lower_bat);

           break;

           case 3:

               /* Write data upper bat 3 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT3U_REG, upper_bat);

               /* Write data lower bat 3 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT3L_REG, lower_bat);

           break;

           case 4:

               /* Write data upper bat 4 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT4U_REG, upper_bat);

               /* Write data lower bat 4 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT4L_REG, lower_bat);

           break;

           case 5:

               /* Write data upper bat 5 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT5U_REG, upper_bat);

               /* Write data lower bat 5 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT5L_REG, lower_bat);

           break;

           case 6:

               /* Write data upper bat 6 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT6U_REG, upper_bat);

               /* Write data lower bat 6 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT6L_REG, lower_bat);

           break;

           case 7:

               /* Write data upper bat 7 with upper bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT7U_REG, upper_bat);

               /* Write data lower bat 7 with lower bat value */
               ESAL_TS_RTE_SPR_WRITE(ESAL_CO_MEM_DBAT7L_REG, lower_bat);

           break;

           default:
           break;

       }   /* switch */

       /* Increment count of data bats used */
       dbat++;

    }   /* if data access */

    *ibat_num = ibat;
    *dbat_num = dbat;
}

#endif /* (CFG_NU_OS_ARCH_PPC_COM_MMU_VARIANT == PPC_MMU_E300_VARIANT) */
