/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * =======================================================================
 *  This program generates an MMU translation table from the contents of
 *  mmuTable[], which is setup by the developer.  The MMU translation table
 *  determines the cache settings for different regions of the address
 *  space.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 */

#include <stdlib.h>
#include <string.h>
#include "Npttypes.h"
#include "reg_def.h"
#include "errhndlr.h"
#ifdef  BSP_ARM9
#include "bsp_refs.h"
#endif
#include "tx_api.h"
#include "mmu_ttb.h"
#include "mmu.h"
#include "nammu.h"
#include "mmu_local.h"
#include "cs.h"
#include "ncc_init.h"

#define SECOND_LEVEL_TABLE_SUBSECTION_SIZE  256

#define ONE_MEG_BIT             20
#define ONE_MEG                 (1024*1024)

#define SIXTY_FOUR_K_BIT        16
#define SIXTY_FOUR_K            (64*1024)

#define FOUR_K_BIT              12
#define FOUR_K                  (4*1024)

#define ONE_K_BIT               10


/*
 * Region types.
 */
#define ROOT            0
#define APPLICATION     1

/*
 * Domains that will be defined.
 */
#define NO_ACCESS_DOMAIN       0
#define CLIENT_DOMAIN          1


#define MMU_TRANSLATION_ALIGNMENT_MASK ((16*1024) - 1)

    
typedef struct region
{
    WORD32 start;    /* address of first byte in region*/
    WORD32 end;      /* size of region*/
    WORD32 phyStart;    /* address of first byte in region*/
    WORD32 phyEnd;      /* size of region*/
    WORD32 pageSize;
    int cacheMode;
    int domain;
    int ap;
    int type;
    struct region *next;
} regionType;


static regionType list = {0,0,0,0,0,0,0,0,0,NULL};
void NAAdjustMmuTable(void);

#ifdef NETOS_ARM_TOOLS
	static char *ttb_start = (char *)&Image$$ttb_table$$ZI$$Base;
	static char *ttb_end = (char *)&Image$$ttb_table$$ZI$$Limit;
#else
#ifndef NETOS_GNU_TOOLS
	static char *ttb_start=(char *)&__ghsbegin_ttb;   /* begin of non-cache section, please see linker files */
	static char *ttb_end=(char *)&__ghsend_ttb; 
#endif
#endif


/*
 * We auto sensed the size of RAM, so we need to adjust the entries
 * in mmuTable[] which have the physical starting address 0x0.
 *
 * mmuTable is in customizedCache.c in each platform directory.
 *
 * This function is called in main after the ram data region has been 
 * initialized, so we can initialize the heap_adjustment variable at this
 * time. heap_adjustment is used to locate the TTB in createTable and
 * to determine the extent of the heap in _sbrk in syscalls.c.
 */
void NAAdjustMmuTable(void)
{
	unsigned int ramSize;
	int i;
	
	ramSize = customizeGetRamSize();
#ifdef  BSP_ARM9
    if (ramSize > (unsigned int)&_NABspRamSize)
	{                            
		heap_adjustment = ramSize - (unsigned int)&_NABspRamSize ;
	}                                                         
	else                                                      
	{                                                         
	    heap_adjustment = 0;                                  
	}                                                         
#endif 
	for (i =0; i < mmuTableSize; i++)
	{
		if (mmuTable[i].phyStart == 0x0)
		{
			mmuTable[i].end &= 0xf0000000;
			mmuTable[i].end |= ramSize -1;
			mmuTable[i].phyEnd = ramSize -1;
		}	
	}
}

/*
 *
 *  Function: regionType *mallocRegionType(void)
 *
 *  Description:
 *
 *      Create a new region.  Region type is set to
 *      application to differentiate it from the
 *      default region created to represent invalid
 *      memory.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      NULL        out of memory
 *      otherwise   pointer to new region item
 *
 */
static regionType *mallocRegionType(void)
{
    int len;
    regionType *result; 

    len = sizeof *result;
    result = (regionType *)malloc (len);
    
    if (result)
    {
        result->type = APPLICATION;
        result->domain = CLIENT_DOMAIN;
    }

    return result;
}




/*
 *
 *  Function: int getMaxPageSize(regionType *region)
 *
 *  Description:
 *
 *      Determine the maximum page size a region can
 *      support.  The start and end addresses have to 
 *      align on page size boundaries.
 *
 *  Parameters:
 *
 *      region      pointer to region to determine max 
 *                  page size of
 *
 *  Return Values:
 *
 *      -1          invalid page size
 *      otherwise   maximum page size for the region
 *
 */
static int getMaxPageSize(regionType *region)
{
    int pageSizes[] = {MMU_PAGE_SIZE_1M, MMU_PAGE_SIZE_64K, MMU_PAGE_SIZE_4K, -1};
    int *pageSize = &pageSizes[0];
    
    do
    {
        if ((region->start % *pageSize) == 0)
        {
            break;
        }
        pageSize++;
    } while (*pageSize != -1);
    
    do
    {
        if (((region->end + 1) % *pageSize) == 0)
        {
            break;
        }
        pageSize++;
    } while (*pageSize != -1);
    
    return *pageSize;
}



/*
 *
 *  Function: int insertRegion(regionType *newRegion)
 *
 *  Description:
 *
 *      Insert a new region into the linked list of
 *      regions.  Verify that the region does not 
 *      overlap any already existing regions.
 *
 *  Parameters:
 *
 *      newRegion       pointer to regionType var to insert
 *
 *  Return Values:
 *
 *      CACHE_SUCCESS         Region has been inserted
 *      CACHE_FAILURE   Region overlaps with another
 *
 */
static int insertRegion(regionType *newRegion)
{
    regionType *p, *prev;
    int desiredEndMod = newRegion->pageSize - 1;
    int oldEnd = newRegion->end;
    int oldEndMod = oldEnd % newRegion->pageSize;
    int result = CACHE_FAILURE;

    if ((newRegion->start % newRegion->pageSize) != 0)
    {
        customizeErrorHandler(ERROR_MMU, ERROR_MMU_MISALIGNED_REGION);
        goto cleanup;
    }
    
    if (oldEndMod != desiredEndMod)
    {
        newRegion->end += desiredEndMod - oldEndMod;
        newRegion->phyEnd += desiredEndMod - oldEndMod;
    }
    
    if (((unsigned int)(newRegion->pageSize)) > (unsigned int)getMaxPageSize(newRegion))
    {
        customizeErrorHandler(ERROR_MMU, ERROR_MMU_INVALID_PAGE_SIZE);
        goto cleanup;
    }
    
    prev = &list;
    
    if (list.next == NULL)
    {
        list.next = mallocRegionType();
        p = list.next;
        p->start = 0;
        p->end = 0xffffffff; 
        p->phyStart = 0;
        p->phyEnd = 0xffffffff; 
        p->pageSize = MMU_PAGE_SIZE_MAX;
        p->cacheMode = MMU_NONBUFFERED;
        p->ap = MMU_NO_ACCESS;
        p->next = NULL;
        p->type = ROOT;
        p->domain = NO_ACCESS_DOMAIN;
    }
    else
    {
        p = prev->next;
    }

    while ((p->end < newRegion->end) && (p->next))  
    {
        prev = p;
        p = p->next;
    }
    if (p->start > newRegion->start)
    {
        customizeErrorHandler(ERROR_MMU, ERROR_MMU_REGIONS_OVERLAP);
        goto cleanup;
    }
    
    if (p->type != ROOT)
    {
        customizeErrorHandler(ERROR_MMU, ERROR_MMU_REGIONS_OVERLAP);
        goto cleanup;
    }

    if (p->start < newRegion->start)
    {
        regionType *splitP = mallocRegionType();
        
        memcpy (splitP, p, sizeof *splitP);
        splitP->phyStart = newRegion->phyStart;
        splitP->start = newRegion->start;
        p->end = newRegion->start - 1;
        p->phyEnd = newRegion->phyStart - 1;
        p->pageSize = getMaxPageSize(p);
        splitP->pageSize = getMaxPageSize(splitP);
        splitP->next = p->next;
        p->next = splitP;
        prev = p;
        p = splitP;
    }

    if (p->end > newRegion->end)
    {
        p->phyStart = newRegion->phyEnd + 1;
        p->start = newRegion->end + 1;
        p->pageSize = getMaxPageSize(p);
        newRegion->next = p;
        prev->next = newRegion;
    }
    else /* if both regions end at the same address*/
    {
        newRegion->next = p->next;
        prev->next = newRegion;
        free (p);
    }
    result = CACHE_SUCCESS;
    
    cleanup:
    
    return result;
}


/*
 *
 *  Function: void fixUpList(void)
 *
 *  Description:
 *
 *      Scan the list of regions for root regions larger
 *      than 1 Meg that have a page size less than 1 Meg.
 *      Break these regions up to create more with page
 *      sizes of 1 Meg.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
static void fixUpList(void)
{
    regionType *p = list.next;
    
    while (p)
    {
        unsigned long size = p->end - p->start;
        unsigned startOffset = p->start % MMU_PAGE_SIZE_MAX;
        unsigned endOffset = p->end % MMU_PAGE_SIZE_MAX;
        unsigned desiredEndOffset = MMU_PAGE_SIZE_MAX - 1;
        
        if ((p->type == ROOT) && (p->pageSize < MMU_PAGE_SIZE_MAX))
        {
            if ((size == MMU_PAGE_SIZE_MAX) && ((p->start % MMU_PAGE_SIZE_MAX) == 0))
            {
                p->pageSize = MMU_PAGE_SIZE_MAX;
            }
            else if (size > MMU_PAGE_SIZE_MAX)
            {
                if (startOffset > 0)
                {
                    regionType *newRegion = mallocRegionType();
                    
                    memcpy (newRegion, p, sizeof *newRegion);
                    newRegion->start = p->start + (MMU_PAGE_SIZE_MAX - startOffset);
                    newRegion->phyStart = p->phyStart + (MMU_PAGE_SIZE_MAX - startOffset);
                    
                    p->end = newRegion->start - 1;
                    p->phyEnd = newRegion->phyStart -1;
                    
                    p->pageSize = getMaxPageSize(p);
                    newRegion->pageSize = getMaxPageSize(newRegion);
                    newRegion->next = p->next;
                    p->next = newRegion;
                    p = newRegion;
                }
                if (((desiredEndOffset - endOffset) != 0) && (size > MMU_PAGE_SIZE_MAX))
                {
                    regionType *newRegion = mallocRegionType();
                    
                    memcpy(newRegion, p, sizeof *newRegion);
                    newRegion->start = p->end - endOffset;
                    newRegion->phyStart = p->phyEnd - endOffset;
                    
                    p->end = newRegion->start - 1;
                    p->phyEnd = newRegion->phyStart - 1;
                    
                    p->pageSize = getMaxPageSize(p);
                    newRegion->pageSize = getMaxPageSize(newRegion);
                    newRegion->next = p->next;
                    p->next = newRegion;
                    p = newRegion;
                }
            }            
        }
        
        p = p->next;
    }
p = list.next;
}

          

/*
 *
 *  Function: int processUserTable(mmuTableType *table, int tableSize)
 *
 *  Description:
 *
 *      Read the configuration file and use the information in it
 *      to create a linked list of memory regions.  Terminate the
 *      program if the config file is faulty.
 *
 *  Parameters:
 *
 *      configFilename  name of file to read
 *
 *  Return Values:
 *
 *      none
 *
 */
static int processUserTable(mmuTableType const *table, int const tableSize)
{
    int tableIndex;
    int result = CACHE_SUCCESS;
    
    for (tableIndex = 0; (tableIndex < tableSize) && (result == CACHE_SUCCESS); tableIndex++)
    {
        regionType *temp = mallocRegionType();
        if (temp == NULL)
        {
            customizeErrorHandler(ERROR_MMU, ERROR_MMU_OUT_OF_RESOURCES);
            goto cleanup;
        }

        /*
         * Copy table entry into linked list entry.  Domain and type
         * are already set by mallocRegionType().
         */
        temp->start = table[tableIndex].start;
        temp->end = table[tableIndex].end;
        temp->phyStart = table[tableIndex].phyStart;
        temp->phyEnd = table[tableIndex].phyEnd;
        temp->pageSize = table[tableIndex].pageSize;
        temp->cacheMode = table[tableIndex].cacheMode;
        temp->ap = table[tableIndex].userAccess;
        result = insertRegion(temp);
    }
    
    fixUpList();
    
    cleanup:
    
    return result;
}




/*
 *
 *  Function: int createTable(void)
 *
 *  Description:
 *
 *      Convert the linked list of memory regions into a TTB
 *      table.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
static int createTable(void)
{
    int tableSize = (ttb_end - ttb_start) / sizeof(WORD32);
    int secondLevelTableSize = tableSize - MMU_FIRST_LEVEL_TABLE_SIZE;
    WORD32 *firstLevelTtb = (WORD32 *)( ttb_start + heap_adjustment);
    WORD32 *secondLevelTtb = &firstLevelTtb[MMU_FIRST_LEVEL_TABLE_SIZE];
    regionType *p = list.next;
    int coarseIndex = 0;
    unsigned int ttbIndex = 0;
    int result = CACHE_FAILURE;
    WORD32 phyBase;
    
    if ((((WORD32) firstLevelTtb) & MMU_TRANSLATION_ALIGNMENT_MASK) != 0)
    {
        customizeErrorHandler(ERROR_MMU, ERROR_MMU_MISALIGNED_TABLE);
        goto cleanup;
    }
    
    
    while (p)
    {
        if (p->pageSize == MMU_PAGE_SIZE_1M)
        {
            for (; ((ttbIndex * MMU_PAGE_SIZE_1M) < p->end) && (ttbIndex < MMU_FIRST_LEVEL_TABLE_SIZE); ttbIndex++)
            {
/*                firstLevelTtb[ttbIndex] = ttbIndex * MMU_PAGE_SIZE_1M; */
                phyBase = (ttbIndex * MMU_PAGE_SIZE_1M) - p->start + p->phyStart;
                firstLevelTtb[ttbIndex] = phyBase;
                
                narm_write_reg(MMU_SECTION, &firstLevelTtb[ttbIndex], AP, p->ap);
                narm_write_reg(MMU_SECTION, &firstLevelTtb[ttbIndex], DOMAIN, p->domain);
                narm_write_reg(MMU_SECTION, &firstLevelTtb[ttbIndex], SB1, 1);
                narm_write_reg(MMU_SECTION, &firstLevelTtb[ttbIndex], CACHE_MODE, p->cacheMode);
                narm_write_reg(MMU_SECTION, &firstLevelTtb[ttbIndex], TYPE, MMU_SECTION_TYPE_VALUE);
            }
            p = p->next;
        }
        else 
        {
            WORD32 pageAddress = p->start;
            
            
            while (((ttbIndex * MMU_PAGE_SIZE_1M) < p->end) && (p->pageSize != MMU_PAGE_SIZE_1M))
            {
                firstLevelTtb[ttbIndex] = (WORD32) &secondLevelTtb[coarseIndex];
                narm_write_reg(MMU_COARSE, &firstLevelTtb[ttbIndex], SBZ1, 0);
                narm_write_reg(MMU_COARSE, &firstLevelTtb[ttbIndex], DOMAIN, p->domain);
                narm_write_reg(MMU_COARSE, &firstLevelTtb[ttbIndex], SB1, 1);
                narm_write_reg(MMU_COARSE, &firstLevelTtb[ttbIndex], SBZ2, 0);
                narm_write_reg(MMU_COARSE, &firstLevelTtb[ttbIndex], TYPE, MMU_COARSE_TYPE_VALUE);
                
                do
                {
                    WORD32 address;
                    
                    for (address = pageAddress; address < (pageAddress + p->pageSize); address += MMU_PAGE_SIZE_4K)
                    {
                        phyBase = address - p->start + p->phyStart;
                        secondLevelTtb[coarseIndex] = phyBase; 
                        narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], AP1, p->ap);
                        narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], AP2, p->ap);
                        narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], AP3, p->ap);
                        narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], AP4, p->ap);
                        narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], CACHE_MODE, p->cacheMode);
                        if (p->pageSize == MMU_PAGE_SIZE_4K)
                        {
                            narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], TYPE, MMU_SMALL_PAGE);
                        }
                        else /* if 64K page size */
                        {
                            narm_write_reg(MMU_COARSE_DESCRIPTOR, &secondLevelTtb[coarseIndex], TYPE, MMU_LARGE_PAGE);
                        }
                        coarseIndex++;
                        if (coarseIndex >= secondLevelTableSize)
                        {
                            customizeErrorHandler(ERROR_MMU, ERROR_MMU_TABLE_OVERFLOW);
                            goto cleanup;
                        }
                    }
                    pageAddress += p->pageSize;
                    if (pageAddress > p->end)
                    {
                        p = p->next;
                    }
                    if (p == NULL)
                    {
                        goto cleanup;
                    }
                } while ((pageAddress % MMU_PAGE_SIZE_1M) != 0);
                ttbIndex++;
            }
        }
    }
    
    result = CACHE_SUCCESS;
    
    cleanup:
    p = list.next;
    
    while (p)
    {
        regionType *next = p->next;
        
        free(p);
        p = next;
    }
        
    return result;
}
    



/*ad*
 * Create MMU Translation Table
 *
 * This routine converts the cache table mmuTable created by the
 * developer in mmuTable.c into the translation tables that the MMU
 * will use.
 *
 * @external
 * @category BSP:Device_Drivers:MMU
 * @since 6.1
 *
 * @return CACHE_SUCCESS        TTB created
 * @return CACHE_FAILURE        Address is not on start of page boundary
*ad*/
static int NACreateMMUTanslationTable(void)

{
    int result = processUserTable(mmuTable, mmuTableSize);
    
    if (result == CACHE_SUCCESS)
    {
        result = createTable();
    }
    
    return result;
}

    

/*ad*
 * Set the cache and access modes for a page of memory
 *
 * This routine modifies the translation table entry for the memory address
 * passed to it to set the new cache and access modes.  A side effect
 * is that data cache is cleaned and invalidated.
 *
 * @since 6.1
 * @category BSP:Device_Drivers:MMU
 * @external
 *
 * @param pageAddress   address of start of page
 * @param cacheMode     cache mode to set
 * @param accessMode    access mode to set
 * @param nextPage      will be loaded with address of next page if function succeeds
 *
 * @return CACHE_SUCCESS        Translation Table entry has been updated
 * @return CACHE_BAD_ADDRESS    Address is not on start of page boundary
*ad*/
int NASetCacheAccessModes(WORD32 pageAddress, WORD32 cacheMode, WORD32 accessMode, WORD32 * nextPage)
{
   int result = CACHE_BAD_ADDRESS;
    WORD32 *ttb=(unsigned long *)(ttb_start + heap_adjustment);
    WORD32 mva = NAVaToMva(pageAddress);
    int ttbIndex = mva >> ONE_MEG_BIT;      /* convert address into first level ttb table index*/
    int type;
    TX_INTERRUPT_SAVE_AREA;

    NA_ASSERT(ttbIndex < MMU_FIRST_LEVEL_TABLE_SIZE);
        
    type = narm_read_reg(MMU_SECTION, &ttb[ttbIndex], TYPE);
    if (type == MMU_SECTION_TYPE_VALUE)
    {
        if ((mva & ((1 << ONE_MEG_BIT) - 1)) == 0)
        {
            TX_DISABLE;
            NAInvalidateAndCleanDCache();
            narm_write_reg(MMU_SECTION, &ttb[ttbIndex], AP, accessMode);
            narm_write_reg(MMU_SECTION, &ttb[ttbIndex], CACHE_MODE, cacheMode);
            NAInvalidateAssociativeTlb();
            NAInvalidateSingleTlbEntry(mva & ~((1 << ONE_K_BIT) - 1));
            TX_RESTORE;
            *nextPage = pageAddress + ONE_MEG;
            result = CACHE_SUCCESS;
        }
    }
    else if (type == MMU_COARSE_TYPE_VALUE)
    {
        WORD32 *tableBase = (WORD32 *) (narm_read_reg(MMU_COARSE_TABLE, &ttb[ttbIndex], BASE_ADDRESS) << MMU_COARSE_TABLE_BASE_ADDRESS_START_BIT);
        int size = narm_read_reg(MMU_COARSE, &ttb[ttbIndex], TYPE);
        int idx = narm_read_reg(MMU_COARSE, &mva, LEVEL_2_TABLE_INDEX);
        
        if (((mva & ((1 << SIXTY_FOUR_K_BIT) - 1)) == 0) && (size == MMU_LARGE_PAGE))
        {
            TX_DISABLE;
            NAInvalidateAndCleanDCache();
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP1, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP2, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP3, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP4, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], CACHE_MODE, cacheMode);
            NAInvalidateAssociativeTlb();
            NAInvalidateSingleTlbEntry(mva & ~((1 << ONE_K_BIT) - 1));
            TX_RESTORE;
            *nextPage = pageAddress + SIXTY_FOUR_K;
            result = CACHE_SUCCESS;
        }
        else if (((mva & ((1 << FOUR_K_BIT) - 1)) == 0) && (size == MMU_SMALL_PAGE))
        {
            TX_DISABLE;
            NAInvalidateAndCleanDCache();
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP1, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP2, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP3, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], AP4, accessMode);
            narm_write_reg(MMU_COARSE_DESCRIPTOR, &tableBase[idx], CACHE_MODE, cacheMode);
            NAInvalidateAssociativeTlb();
            NAInvalidateSingleTlbEntry(mva & ~((1 << ONE_K_BIT) - 1));
            TX_RESTORE;
            *nextPage = pageAddress + FOUR_K;
            result = CACHE_SUCCESS;
        }
    }
    
    return result;
}
    



/*ad*
 * Configure the memory management unit and turn it on
 * 
 * This function is used internally by NET+OS to initialize the MMU. It
 * builds the MMU translation table, initializes the MMU, and enables data cache.
 * Instruction cache will already have been enabled in nccInit().  The MMU
 * translation table is built based on entries in mmuTable, which you should 
 * edit to suite your particular application.
 *
 * @note    The memory protection system is not enabled until data cache is
 *          turned on.  This means that if data cache is not turned on, then
 *          accesses to invalid addresses may cause the processor to hang 
 *          instead of generating abort exceptions.
 *
 * @external
 * @category BSP:Device_Drivers:MMU
 * @since 6.1 
 *
 * @see @link BSP:Device_Drivers:MMU:mmuTable
*ad*/
void NAEnableMmu(void)
{
    NACreateMMUTanslationTable();

    /* Enable the memory management unit */
    NAInitMMU();
    
    NAEnableDCache();       /* cache must be turned on for memory protection to work*/

    return;
}




