| /******************************************************************************* |
| Copyright (C) Marvell International Ltd. and its affiliates |
| |
| ******************************************************************************** |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| |
| *******************************************************************************/ |
| |
| /* Code for setting up pagetables or the protection unit, |
| * and enabling the cache. */ |
| |
| #include <config.h> |
| #include <common.h> |
| #include <asm/types.h> |
| #include <command.h> |
| |
| #include "mvSysHwConfig.h" |
| #if defined(MV_INCLUDE_MONT_EXT) && defined (MV_INCLUDE_MONT_MPU) |
| |
| |
| #include "mvTypes.h" |
| #include "mvCtrlEnvLib.h" |
| #include "mvCpuIf.h" |
| #include "cpu/mvCpu.h" |
| |
| |
| /* This file refers to the A.R.M.--The ARM Architecture Reference Manual */ |
| |
| |
| typedef enum _access_type{ |
| |
| /* priveliged - user */ |
| NO_NO = 0 , /* No access _ No Access */ |
| RW_NO = 1, /* Read/Write _ No Access */ |
| RW_RO = 2 , /* Read/Write _ Read Only */ |
| RW_RW = 3, /* Read/Write _ Read/Write */ |
| RO_NO = 5 , /* Read Only _ No Access */ |
| RO_RO = 6 /* Read Only _ Read Only */ |
| |
| }ACCESS_TYPE; |
| |
| |
| typedef enum _region_size{ |
| |
| REG_4KB = 0xB, |
| REG_8KB = 0xC, |
| REG_16KB = 0xD, |
| REG_32KB = 0xE, |
| REG_64KB = 0xF, |
| REG_128KB = 0x10, |
| REG_256KB = 0x11, |
| REG_512KB = 0x12, |
| REG_1MB = 0x13, |
| REG_2MB = 0x14, |
| REG_4MB = 0x15, |
| REG_8MB = 0x16, |
| REG_16MB = 0x17, |
| REG_32MB = 0x18, |
| REG_64MB = 0x19, |
| REG_128MB = 0x1A, |
| REG_256MB = 0x1B, |
| REG_512MB = 0x1C, |
| REG_1GB = 0x1D, |
| REG_2GB = 0x1E, |
| REG_4GB = 0x1F |
| |
| }REGION_SIZE; |
| |
| typedef unsigned char bool; |
| |
| typedef struct _mpu_region |
| { |
| unsigned int base; |
| REGION_SIZE size; |
| bool iCache; |
| bool dCache; |
| bool wb; |
| ACCESS_TYPE dAccess; |
| ACCESS_TYPE iAccess; |
| |
| }MPU_REGION; |
| |
| |
| typedef enum _cache_type{ |
| |
| D_CACHE, |
| I_CACHE |
| |
| }CACHE_TYPE; |
| |
| |
| typedef enum _mem_type{ |
| |
| D_MEM, |
| I_MEM |
| |
| }MEM_TYPE; |
| |
| static unsigned int sizeToBits(unsigned int size) |
| { |
| |
| switch (size) |
| { |
| case 0x00001000: |
| return REG_4KB; |
| break; |
| case 0x00002000: |
| return REG_8KB; |
| break; |
| case 0x00004000: |
| return REG_16KB; |
| break; |
| case 0x00008000: |
| return REG_32KB; |
| break; |
| case 0x00010000: |
| return REG_64KB; |
| break; |
| case 0x00020000: |
| return REG_128KB; |
| break; |
| case 0x00040000: |
| return REG_256KB; |
| break; |
| case 0x00080000: |
| return REG_512KB; |
| break; |
| case 0x00100000: |
| return REG_1MB; |
| break; |
| case 0x00200000: |
| return REG_2MB; |
| break; |
| case 0x00400000: |
| return REG_4MB; |
| break; |
| case 0x00800000: |
| return REG_8MB; |
| break; |
| case 0x01000000: |
| return REG_16MB; |
| break; |
| case 0x02000000: |
| return REG_32MB; |
| break; |
| case 0x04000000: |
| return REG_64MB; |
| break; |
| case 0x08000000: |
| return REG_128MB; |
| break; |
| case 0x10000000: |
| return REG_256MB; |
| break; |
| case 0x20000000: |
| return REG_512MB; |
| break; |
| case 0x40000000: |
| return REG_1GB; |
| break; |
| case 0x80000000: |
| return REG_2GB; |
| break; |
| |
| } |
| return 0; |
| } |
| |
| static void printSizeOfRegion(REGION_SIZE bits) |
| { |
| |
| switch (bits) |
| { |
| case REG_4KB: |
| printf("4KB"); |
| break; |
| case REG_8KB: |
| printf("8KB"); |
| break; |
| case REG_16KB: |
| printf("16KB"); |
| break; |
| case REG_32KB: |
| printf("32KB"); |
| break; |
| case REG_64KB: |
| printf("64KB"); |
| break; |
| case REG_128KB: |
| printf("128KB"); |
| break; |
| case REG_256KB: |
| printf("256KB"); |
| break; |
| case REG_512KB: |
| printf("512KB"); |
| break; |
| case REG_1MB: |
| printf("1MB"); |
| break; |
| case REG_2MB: |
| printf("2MB"); |
| break; |
| case REG_4MB: |
| printf("4MB"); |
| break; |
| case REG_8MB: |
| printf("8MB"); |
| break; |
| case REG_16MB: |
| printf("16MB"); |
| break; |
| case REG_32MB: |
| printf("32MB"); |
| break; |
| case REG_64MB: |
| printf("64MB"); |
| break; |
| case REG_128MB: |
| printf("128MB"); |
| break; |
| case REG_256MB: |
| printf("256MB"); |
| break; |
| case REG_512MB: |
| printf("512MB"); |
| break; |
| case REG_1GB: |
| printf("1GB"); |
| case REG_2GB: |
| printf("2GB"); |
| break; |
| case REG_4GB: |
| printf("4GB"); |
| break; |
| } |
| } |
| |
| /* |
| get Control register |
| */ |
| unsigned int get_control(void) |
| { |
| unsigned int value; |
| |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c1, c0, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| |
| return value; |
| } |
| |
| /* |
| set Control register |
| */ |
| void set_control(unsigned int value) |
| { |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c1, c0, 0\n" |
| : |
| : "r" (value)); |
| } |
| |
| /* |
| get Write Buffer Configuration |
| */ |
| static unsigned int get_wb(void) |
| { |
| unsigned int value; |
| |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c3, c0, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| |
| return value; |
| } |
| |
| /* |
| set Write Buffer Configuration |
| */ |
| static void set_wb(unsigned int value) |
| { |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c3, c0, 0\n" |
| : |
| : "r" (value)); |
| } |
| |
| /* |
| Read cache configuration |
| */ |
| |
| static unsigned int get_cache_config(CACHE_TYPE type) |
| { |
| unsigned int value = 0; |
| |
| switch (type) |
| { |
| case D_CACHE: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c2, c0, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| |
| break; |
| case I_CACHE: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c2, c0, 1\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| |
| break; |
| |
| } |
| |
| return value; |
| } |
| |
| /* |
| Write cache configuration |
| */ |
| |
| static void set_cache_config(CACHE_TYPE type,unsigned int value) |
| { |
| switch (type) { |
| case D_CACHE: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c2, c0, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case I_CACHE: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c2, c0, 1\n" |
| : |
| : "r" (value)); |
| break; |
| } |
| } |
| |
| /* |
| Read access permision |
| */ |
| |
| static unsigned int get_access(MEM_TYPE type) |
| { |
| unsigned int value = 0; |
| |
| switch (type) { |
| case D_MEM: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c5, c0, 2\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case I_MEM: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c5, c0, 3\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| } |
| |
| return value; |
| } |
| |
| /* |
| Write cache configuration |
| */ |
| |
| static void set_access(MEM_TYPE type,unsigned int value) |
| { |
| switch (type) { |
| case D_MEM: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c5, c0, 2\n" |
| : |
| : "r" (value)); |
| break; |
| case I_MEM: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c5, c0, 3\n" |
| : |
| : "r" (value)); |
| break; |
| } |
| } |
| |
| /* |
| get protection region size\base\enable |
| */ |
| |
| unsigned int get_prot_attrib(int region) |
| { |
| unsigned int value = 0; |
| |
| switch (region) { |
| case 0: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c0, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 1: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c1, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 2: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c2, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 3: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c3, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 4: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c4, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 5: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c5, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 6: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c6, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| case 7: |
| __asm__ __volatile__( |
| "mrc p15, 0, %0, c6, c7, 0\n" |
| : "=r" (value) |
| : |
| : "memory"); |
| break; |
| } |
| return value; |
| } |
| |
| /* |
| set protection region size\base\enable |
| */ |
| |
| static void set_prot_attrib(int region,unsigned int value) |
| { |
| switch (region) { |
| case 0: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c0, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 1: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c1, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 2: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c2, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 3: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c3, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 4: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c4, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 5: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c5, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 6: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c6, 0\n" |
| : |
| : "r" (value)); |
| break; |
| case 7: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c6, c7, 0\n" |
| : |
| : "r" (value)); |
| break; |
| } |
| } |
| |
| |
| static void createMPUEntry(int region,MPU_REGION *mpuEntry) |
| { |
| unsigned int value = 0; |
| |
| /* set data cache attributes */ |
| value = get_cache_config(D_CACHE); |
| |
| if (mpuEntry->dCache) { |
| value |= (1<< region); |
| } else { |
| value &= ~(1<< region); |
| } |
| |
| set_cache_config(D_CACHE,value); |
| |
| /* set instruction cache attributes */ |
| value = get_cache_config(I_CACHE); |
| |
| if (mpuEntry->iCache) { |
| value |= (1<< region); |
| } else { |
| value &= ~(1<< region); |
| } |
| |
| set_cache_config(I_CACHE,value); |
| |
| /* set write buffer */ |
| |
| value = get_wb(); |
| |
| if (mpuEntry->wb) { |
| value |= (1<< region); |
| } else { |
| value &= ~(1<< region); |
| } |
| |
| set_wb(value); |
| |
| /* set access permision for data accesses*/ |
| |
| value = get_access(D_MEM); |
| value &= ~(0xf << (region*4)); |
| value |= (mpuEntry->dAccess << (region*4)); |
| set_access(D_MEM , value); |
| |
| /* set access permision for instruction accesses*/ |
| |
| value = get_access(I_MEM); |
| value &= ~(0xf << (region*4)); |
| value |= (mpuEntry->iAccess << (region*4)); |
| set_access(I_MEM, value); |
| |
| /*set base and size and enable*/ |
| value = 0; |
| value |= (mpuEntry->base) & (0xFFFFF << 12); |
| value |= ( mpuEntry->size << 1); |
| value |= 1; |
| |
| set_prot_attrib(region,value); |
| } |
| |
| |
| /* These are all the bits currently defined for the control register */ |
| /* A.R.M. 7.4.2 */ |
| #define MPU_V 0x2000 /* alternae vector select */ |
| #define MPU_I 0x1000 /* Instruction cache */ |
| #define MPU_B 0x0080 /* big endian */ |
| #define MPU_RES 0x00F79 /* reserved bits should be 1 */ |
| #define MPU_C 0x0004 /* data cache */ |
| #define MPU_P 0x0001 /* protection unit */ |
| |
| |
| |
| /* |
| * The functions below take arguments to specify which "caches" the |
| * action is to be directed at. For the I-cache, pass "MMU_I". For |
| * the D-cache, "MMU_C". For both, pass "MMU_ID". For combined ID-Cache |
| * processors, use "MMU_C" |
| */ |
| #define MPU_ID (MPU_I + MPU_C) |
| |
| |
| int mpuMap(void) |
| { |
| unsigned int value; |
| int region; |
| |
| value = get_control(); |
| |
| if (value & MPU_P) { |
| printf("\nProtection Unit:\n"); |
| |
| for (region = 1 ; region < 8 ; region++) { |
| value = get_prot_attrib(region); |
| /* check if region is enabled */ |
| if (value & 1) { |
| printf("region %d base=0x%08x size =",region,value & (0xFFFFF << 12)); |
| printSizeOfRegion(((value & (0x1f << 1)) >> 1)); |
| |
| value = get_cache_config(D_CACHE); |
| |
| if (value & (1 << region)) { |
| printf(" :DCache enabled - "); |
| } else { |
| printf(" :DCache disabled - "); |
| } |
| |
| value = get_cache_config(I_CACHE); |
| |
| if (value & (1 << region)) { |
| printf("ICache enabled - "); |
| } else { |
| printf("ICache disabled - "); |
| } |
| |
| value = get_wb(); |
| |
| if (value & (1 << region)) { |
| printf("Bufferable"); |
| } else { |
| printf("non Bufferable"); |
| } |
| |
| printf("\n"); |
| } |
| } |
| } else { |
| printf("MPU is disabled\n"); |
| } |
| |
| return 1; |
| } |
| |
| /* Flush the cache(s). |
| */ |
| inline void mpuInvCache(unsigned caches) |
| { |
| unsigned long dummy = 0; |
| |
| switch (caches) { |
| case MPU_C: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c7, c6, 0\n" |
| : |
| : "r" (dummy)); |
| break; |
| case MPU_I: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c7, c5, 0\n" |
| : |
| : "r" (dummy)); |
| break; |
| case MPU_ID: |
| __asm__ __volatile__( |
| "mcr p15, 0, %0, c7, c7, 0\n" |
| : |
| : "r" (dummy)); |
| break; |
| } |
| } |
| |
| /* Enable the cache/MMU/TLB etc. |
| */ |
| inline void _cpuCfgEnable(unsigned long flags, MV_BOOL enable) |
| { |
| unsigned long tmp; |
| |
| if (enable == MV_TRUE) { |
| asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (tmp)); |
| tmp |= flags; |
| asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (tmp)); |
| } else { |
| asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (tmp)); |
| tmp &= ~flags; |
| asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (tmp)); |
| } |
| } |
| |
| /* Disable the I/D cache. |
| */ |
| |
| inline void _disableIDCache(void) |
| { |
| unsigned long tmp; |
| asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (tmp)); |
| tmp &= ~MPU_ID; |
| asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (tmp)); |
| |
| /* invalidate I/D-cache */ |
| tmp = 0; |
| asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (tmp)); |
| |
| } |
| |
| |
| void MPU_Init(void) |
| { |
| MPU_REGION mpuEntry; |
| #if defined(MV_INCLUDE_SDRAM_CS1) |
| MV_CPU_DEC_WIN win; |
| MV_TARGET target; |
| unsigned int totalSize=0; |
| #endif |
| int region=0; |
| char *env; |
| |
| printf("Intializing Protection Unit\n"); |
| _disableIDCache(); |
| |
| /* enable I cache - doesn't need MPU enabling */ |
| /*_enable(MPU_RES);*/ |
| |
| /* set region 0 - include all memory and have I and D caches disabled |
| and write buffer disabled */ |
| |
| mpuEntry.base = 0; |
| mpuEntry.size = REG_4GB; |
| mpuEntry.dCache = 0; |
| mpuEntry.iCache = 0; |
| mpuEntry.wb = 0; |
| mpuEntry.iAccess = NO_NO; |
| mpuEntry.dAccess = NO_NO; |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| /* set region 1 - CS0 DRAM region */ |
| |
| mpuEntry.base = mvCpuIfTargetWinBaseLowGet(SDRAM_CS0); |
| |
| mpuEntry.size = sizeToBits(mvCpuIfTargetWinSizeGet(SDRAM_CS0)); |
| |
| mpuEntry.dCache = 0; |
| mpuEntry.iCache = 1; |
| mpuEntry.wb = 0; |
| |
| mpuEntry.iAccess = RW_RW; |
| mpuEntry.dAccess = RW_RW; |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| |
| /* set region 2 - CS0-CS3 DRAM region */ |
| #if defined(MV_INCLUDE_SDRAM_CS1) |
| for (target = SDRAM_CS1 ; target < MV_DRAM_MAX_CS ; target++) { |
| mvCpuIfTargetWinGet(target, &win); |
| |
| if (win.enable) break; |
| } |
| |
| target = SDRAM_CS1; |
| mpuEntry.base = mvCpuIfTargetWinBaseLowGet(target); |
| |
| for (target = SDRAM_CS1; target < MV_DRAM_MAX_CS ; target++) |
| totalSize += mvCpuIfTargetWinSizeGet(target); |
| |
| mpuEntry.size = sizeToBits(totalSize); |
| |
| mpuEntry.dCache = 0; |
| mpuEntry.iCache = 0; |
| mpuEntry.wb = 0; |
| |
| mpuEntry.iAccess = RW_RW; |
| mpuEntry.dAccess = RW_RW; |
| #endif |
| |
| env = getenv("dramCached"); |
| |
| if((!env)||( (strcmp(env,"yes") == 0) || (strcmp(env,"Yes") == 0) )) { |
| setenv("dramCached","yes"); |
| |
| mpuEntry.dCache = 1; |
| mpuEntry.iCache = 1; |
| mpuEntry.wb = 1; |
| } |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| /* set region 3 - devices region */ |
| mpuEntry.base = 0xF0000000; |
| |
| mpuEntry.size = REG_256MB; |
| |
| mpuEntry.dCache = 0; |
| mpuEntry.iCache = 0; |
| mpuEntry.wb = 0; |
| |
| mpuEntry.iAccess = RW_RW; |
| mpuEntry.dAccess = RW_RW; |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| /* set region 4 - pci region */ |
| mpuEntry.base = 0x90000000; |
| |
| mpuEntry.size = REG_256MB; |
| |
| mpuEntry.dCache = 0; |
| mpuEntry.iCache = 0; |
| mpuEntry.wb = 0; |
| |
| mpuEntry.iAccess = RW_RW; |
| mpuEntry.dAccess = RW_RW; |
| |
| env = getenv("pciCached"); |
| |
| if(!env) { |
| setenv("pciCached","no"); |
| } else { |
| if( ( (strcmp(env,"yes") == 0) || (strcmp(env,"Yes") == 0) ) ) { |
| mpuEntry.dCache = 1; |
| mpuEntry.iCache = 1; |
| mpuEntry.wb = 1; |
| } |
| } |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| /* set region 5 - stack pointer */ |
| mpuEntry.base = 0xF00000; |
| |
| mpuEntry.size = REG_64KB; |
| |
| mpuEntry.dCache = 1; |
| mpuEntry.iCache = 1; |
| mpuEntry.wb = 1; |
| |
| mpuEntry.iAccess = RW_RW; |
| mpuEntry.dAccess = RW_RW; |
| |
| |
| createMPUEntry(region++ , &mpuEntry); |
| |
| |
| /*mpuInvCache(MPU_ID);*/ |
| /* write to control register :- |
| * I-cache on, 32-bit data and program space, |
| * write-buffer on, D-cache on, MMU on |
| */ |
| _cpuCfgEnable((MPU_I|MPU_C|MPU_P|MPU_RES), MV_TRUE); |
| |
| return ; |
| } |
| |
| #endif /* MV_INCLUDE_MONT_EXT */ |