/****************************************************************************** | |
* | |
* Copyright 2013 Altera Corporation. All Rights Reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* 2. Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* | |
* 3. The name of the author may not be used to endorse or promote products | |
* derived from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO | |
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | |
* OF SUCH DAMAGE. | |
* | |
******************************************************************************/ | |
#include "alt_cache.h" | |
#include "alt_interrupt_common.h" | |
#include "alt_mmu.h" | |
#include "socal/alt_sysmgr.h" | |
#include "socal/hps.h" | |
#include "socal/socal.h" | |
#include <stdio.h> | |
#include <inttypes.h> | |
///// | |
// NOTE: To enable debugging output, delete the next line and uncomment the | |
// line after. | |
#define dprintf(...) | |
// #define dprintf printf | |
///// | |
#ifndef MIN | |
#define MIN(a, b) ((a) > (b) ? (b) : (a)) | |
#endif | |
///// | |
// System Level API here | |
ALT_STATUS_CODE alt_cache_system_enable(void) | |
{ | |
alt_cache_l1_enable_all(); | |
alt_cache_l2_init(); | |
alt_cache_l2_prefetch_enable(); | |
alt_cache_l2_parity_enable(); | |
alt_cache_l2_enable(); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_system_disable(void) | |
{ | |
alt_cache_l2_disable(); | |
alt_cache_l2_parity_disable(); | |
alt_cache_l2_prefetch_disable(); | |
alt_cache_l2_uninit(); | |
alt_cache_l1_disable_all(); | |
return ALT_E_SUCCESS; | |
} | |
#define ALT_CPU_PAR_FS_VALUE_GET(par) ((par >> 1) & 0x3f) | |
#define ALT_CPU_PAR_SS_SET_MSK 0x00000002 | |
#define ALT_CPU_PAR_F_SET_MSK 0x00000001 | |
static inline uintptr_t alt_mmu_va_to_pa(void * va, uint32_t * seglength, uint32_t * dfsr) | |
{ | |
#if ALT_CACHE_SUPPORT_NON_FLAT_VIRTUAL_MEMORY | |
uintptr_t pa = 0; | |
// ATS1CPR [Address Translate Stage 1 Current state PL1 Read]; See ARMv7-A,R, section B4.2.4. | |
// ISB | |
// Read the PAR | |
#ifdef __ARMCC_VERSION | |
__asm("mcr p15, 0, va, c7, c8, 0"); | |
__asm("isb"); | |
__asm("mrc p15, 0, pa, c7, c4, 0"); | |
#else | |
__asm("mcr p15, 0, %0, c7, c8, 0" : : "r" (va)); | |
__asm("isb"); | |
__asm("mrc p15, 0, %0, c7, c4, 0" : "=r" (pa)); | |
#endif | |
if (pa & ALT_CPU_PAR_F_SET_MSK) | |
{ | |
// If the F bit (fault) is set, then report the error. | |
// Extract the PAR::FS field value. | |
uint32_t fs = ALT_CPU_PAR_FS_VALUE_GET(pa); | |
// Translate PAR::FS[5:0] (field value) or PAR[6:1] => DFSR[12,10,3:0] to report error. | |
*dfsr = ((fs & 0x20) << 7) | // bit 12 | |
((fs & 0x10) << 6) | // bit 10 | |
((fs & 0x0f) << 0); // bit 3:0 | |
dprintf("DEBUG[cache][va->pa]: Fault detected. DFSR = 0x%" PRIx32 ".\n", dfsr); | |
} | |
else if (pa & ALT_CPU_PAR_SS_SET_MSK) | |
{ | |
// If the page is a supersection, PAR contains PA[31:24]. VA contains PA[23:0]. | |
uint32_t offset = (uintptr_t)va & (ALT_MMU_SUPERSECTION_SIZE - 1); | |
pa &= ~(ALT_MMU_SUPERSECTION_SIZE - 1); | |
pa |= offset; | |
dprintf("DEBUG[cache][va->pa]: pa[SS] = 0x%x; offset = 0x%" PRIx32 ".\n", | |
pa & ~(ALT_MMU_SUPERSECTION_SIZE - 1), | |
offset); | |
*seglength = ALT_MMU_SUPERSECTION_SIZE - offset; | |
*dfsr = 0; | |
} | |
else | |
{ | |
// If the page is not a supersection, PAR contains PA[31:12]. VA contains PA[11:0]. | |
uint32_t offset = (uintptr_t)va & (ALT_MMU_SMALL_PAGE_SIZE - 1); | |
pa &= ~(ALT_MMU_SMALL_PAGE_SIZE - 1); | |
pa |= offset; | |
dprintf("DEBUG[cache][va->pa]: pa[page] = 0x%x; offset = 0x%" PRIx32 ".\n", | |
pa & ~(ALT_MMU_SMALL_PAGE_SIZE - 1), | |
offset); | |
*seglength = ALT_MMU_SMALL_PAGE_SIZE - offset; | |
*dfsr = 0; | |
} | |
return pa; | |
#else | |
*seglength = 0xffffffff - (uintptr_t)va + 1; | |
*dfsr = 0; | |
return (uintptr_t)va; | |
#endif | |
} | |
// Internal functions that expect all inputs to be aligned properly. | |
static void alt_cache_l1_data_invalidate_helper(void * vaddress, size_t length); | |
static void alt_cache_l1_data_clean_helper(void * vaddress, size_t length); | |
static void alt_cache_l1_data_purge_helper(void * vaddress, size_t length); | |
static ALT_STATUS_CODE alt_cache_l2_invalidate_helper(uintptr_t paddress, size_t length); | |
static ALT_STATUS_CODE alt_cache_l2_clean_helper(uintptr_t paddress, size_t length); | |
static ALT_STATUS_CODE alt_cache_l2_purge_helper(uintptr_t paddress, size_t length); | |
ALT_STATUS_CODE alt_cache_system_invalidate(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - address and length are on the cache boundaries | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
dprintf("DEBUG[cache][sys]: invalidate: vaddr = %p; length = 0x%x.\n", vaddress, length); | |
char * va = vaddress; | |
while (length) | |
{ | |
uint32_t dfsr = 0; | |
uint32_t seg_size = 0; | |
uintptr_t pa = alt_mmu_va_to_pa(va, &seg_size, &dfsr); | |
if (dfsr) | |
{ | |
return ALT_E_ERROR; | |
} | |
seg_size = MIN(length, seg_size); | |
dprintf("DEBUG[cache][sys]: (loop): va = %p; pa = 0x%x; size = 0x%" PRIx32 ".\n", va, pa, seg_size); | |
alt_cache_l2_invalidate_helper(pa, seg_size); | |
alt_cache_l2_sync(); | |
alt_cache_l1_data_invalidate_helper(va, seg_size); | |
// __asm("dsb") handled by l1_data_invalidate(). | |
va += seg_size; | |
length -= seg_size; | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_system_clean(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - address and length are on the cache boundaries | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
dprintf("DEBUG[cache][sys]: clean: vaddr = %p; length = 0x%x.\n", vaddress, length); | |
char * va = vaddress; | |
while (length) | |
{ | |
uint32_t dfsr = 0; | |
uint32_t seg_size = 0; | |
uintptr_t pa = alt_mmu_va_to_pa(va, &seg_size, &dfsr); | |
if (dfsr) | |
{ | |
return ALT_E_ERROR; | |
} | |
seg_size = MIN(length, seg_size); | |
dprintf("DEBUG[cache][sys]: (loop): va = %p; pa = 0x%x; size = 0x%" PRIx32 ".\n", va, pa, seg_size); | |
alt_cache_l1_data_clean_helper(va, seg_size); | |
// __asm("dsb") handled by l1_data_clean(). | |
alt_cache_l2_clean_helper(pa, seg_size); | |
alt_cache_l2_sync(); | |
va += seg_size; | |
length -= seg_size; | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_system_purge(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - address and length are on the cache boundaries | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
dprintf("DEBUG[cache][sys]: clean: vaddr = %p; length = 0x%x.\n", vaddress, length); | |
char * va = vaddress; | |
while (length) | |
{ | |
uint32_t dfsr = 0; | |
uint32_t seg_size = 0; | |
uintptr_t pa = alt_mmu_va_to_pa(va, &seg_size, &dfsr); | |
if (dfsr) | |
{ | |
return ALT_E_ERROR; | |
} | |
seg_size = MIN(length, seg_size); | |
dprintf("DEBUG[cache][sys]: (loop): va = %p; pa = 0x%x; size = 0x%" PRIx32 ".\n", va, pa, seg_size); | |
alt_cache_l1_data_clean_helper(va, seg_size); | |
// __asm("dsb") handled by l1_data_clean(). | |
alt_cache_l2_purge_helper(pa, seg_size); | |
alt_cache_l2_sync(); | |
alt_cache_l1_data_invalidate_helper(va, seg_size); | |
// __asm("dsb") handled by l1_data_invalidate(). | |
va += seg_size; | |
length -= seg_size; | |
} | |
return ALT_E_SUCCESS; | |
} | |
///// | |
// | |
// Stand-in SoCAL for ARM CPU registers needed for L1 cache functions. | |
// | |
// System Control Register. See Cortex-A9, section 4.3.9. | |
#define ALT_CPU_SCTLR_TE_SET_MSK (1 << 30) | |
#define ALT_CPU_SCTLR_AFE_SET_MSK (1 << 29) | |
#define ALT_CPU_SCTLR_TRE_SET_MSK (1 << 28) | |
#define ALT_CPU_SCTLR_NMFI_SET_MSK (1 << 27) | |
#define ALT_CPU_SCTLR_EE_SET_MSK (1 << 25) | |
#define ALT_CPU_SCTLR_HA_SET_MSK (1 << 17) | |
#define ALT_CPU_SCTLR_RR_SET_MSK (1 << 14) | |
#define ALT_CPU_SCTLR_V_SET_MSK (1 << 13) | |
#define ALT_CPU_SCTLR_I_SET_MSK (1 << 12) | |
#define ALT_CPU_SCTLR_Z_SET_MSK (1 << 11) | |
#define ALT_CPU_SCTLR_SW_SET_MSK (1 << 10) | |
#define ALT_CPU_SCTLR_C_SET_MSK (1 << 2) | |
#define ALT_CPU_SCTLR_A_SET_MSK (1 << 1) | |
#define ALT_CPU_SCTLR_M_SET_MSK (1 << 0) | |
// Auxiliary Control Register. See Cortex-A9, section 4.3.10. | |
#define ALT_CPU_ACTLR_PARITYON_SET_MSK (1 << 9) | |
#define ALT_CPU_ACTLR_ALLOCINONEWAY_SET_MSK (1 << 8) | |
#define ALT_CPU_ACTLR_EXCL_SET_MSK (1 << 7) | |
#define ALT_CPU_ACTLR_SMP_SET_MSK (1 << 6) | |
#define ALT_CPU_ACTLR_WRITEFULLLINEZEROS_SET_MSK (1 << 3) | |
#define ALT_CPU_ACTLR_L1PREFETCHEN_SET_MSK (1 << 2) | |
#define ALT_CPU_ACTLR_L2PREFETCHEN_SET_MSK (1 << 1) | |
#define ALT_CPU_ACTLR_FW_SET_MSK (1 << 0) | |
// Cache Size Selection Register. See Cortex-A9, section 4.3.8. | |
#define ALT_CPU_ACTLR_LEVEL_SET_MSK (7 << 1) | |
#define ALT_CPU_ACTLR_IND_SET_MSK (1 << 0) | |
// Cache Size Identification Register. See Cortex-A9, section 4.3.5. | |
#define ALT_CPU_CCSIDR_WT_SET_MSK (0x1 << 31) | |
#define ALT_CPU_CCSIDR_WB_SET_MSK (0x1 << 30) | |
#define ALT_CPU_CCSIDR_RA_SET_MSK (0x1 << 29) | |
#define ALT_CPU_CCSIDR_WA_SET_MSK (0x1 << 28) | |
#define ALT_CPU_CCSIDR_NUMSETS_SET_MSK (0x7fff << 13) | |
#define ALT_CPU_CCSIDR_ASSOCIATIVITY_SET_MSK (0x3ff << 3) | |
#define ALT_CPU_CCSIDR_LINESIZE_SET_MSK (0x7 << 0) | |
#define ALT_CPU_CCSIDR_NUMSETS_VALUE_GET(value) (((value) & ALT_CPU_CCSIDR_NUMSETS_SET_MSK) >> 13) | |
#define ALT_CPU_CCSIDR_ASSOCIATIVITY_VALUE_GET(value) (((value) & ALT_CPU_CCSIDR_ASSOCIATIVITY_SET_MSK) >> 3) | |
#define ALT_CPU_CCSIDR_LINESIZE_VALUE_GET(value) (((value) & ALT_CPU_CCSIDR_LINESIZE_SET_MSK) >> 0) | |
///// | |
static inline __attribute__((always_inline)) uint32_t sctlr_read_helper(void) | |
{ | |
uint32_t sctlr; | |
#ifdef __ARMCC_VERSION | |
__asm("MRC p15, 0, sctlr, c1, c0, 0"); | |
#else | |
__asm("MRC p15, 0, %0, c1, c0, 0" : "=r" (sctlr)); | |
#endif | |
return sctlr; | |
} | |
static inline __attribute__((always_inline)) void sctlr_write_helper(uint32_t sctlr) | |
{ | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, sctlr, c1, c0, 0"); | |
#else | |
__asm("MCR p15, 0, %0, c1, c0, 0" : : "r" (sctlr)); | |
#endif | |
} | |
static inline __attribute__((always_inline)) uint32_t actlr_read_helper(void) | |
{ | |
uint32_t actlr; | |
#ifdef __ARMCC_VERSION | |
__asm("MRC p15, 0, actlr, c1, c0, 1"); | |
#else | |
__asm("MRC p15, 0, %0, c1, c0, 1" : "=r" (actlr)); | |
#endif | |
return actlr; | |
} | |
static inline __attribute__((always_inline)) void actlr_write_helper(uint32_t actlr) | |
{ | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, actlr, c1, c0, 1"); | |
#else | |
__asm("MCR p15, 0, %0, c1, c0, 1" : : "r" (actlr)); | |
#endif | |
} | |
static void cssidr_decode_helper(bool query_i_cache, uint32_t * log2_L, uint32_t * log2_A, uint32_t * log2_S) | |
{ | |
// First query the D cache information using CSSELR to select the data cache | |
// See ARM Cortex-A9 TRM, section 4.3.8. | |
// Wait for the CSSELR to flush. | |
// Then use CSSIDR to get the cache charateristics | |
// See ARMv7-A,R, section B4.1.19 | |
uint32_t csselr; | |
uint32_t cssidr; | |
if (query_i_cache) | |
{ | |
csselr = ALT_CPU_ACTLR_IND_SET_MSK; | |
} | |
else | |
{ | |
csselr = 0; | |
} | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 2, csselr, c0, c0, 0"); | |
__asm("ISB"); | |
__asm("MRC p15, 1, cssidr, c0, c0, 0"); | |
#else | |
__asm("MCR p15, 2, %0, c0, c0, 0" : : "r" (csselr)); | |
__asm("ISB"); | |
__asm("MRC p15, 1, %0, c0, c0, 0" : "=r" (cssidr)); | |
#endif | |
// Extract the associativity, line length, and number of sets. | |
int linesize = ALT_CPU_CCSIDR_LINESIZE_VALUE_GET(cssidr) + 2 + 2; // {log2(line length in words) - 2} + 2 + 2 => (... in bytes) | |
int associativity = ALT_CPU_CCSIDR_ASSOCIATIVITY_VALUE_GET(cssidr) + 1; | |
int numsets = ALT_CPU_CCSIDR_NUMSETS_VALUE_GET(cssidr) + 1; | |
// Determine the log2 of the associativity and numsets, rounded up | |
int L = linesize; // log2(line length in bytes) | |
int A = 0; // log2(associativity) rounded up | |
int S = 0; // log2(number of sets) rounded up | |
while ((1 << A) < associativity) | |
{ | |
++A; | |
} | |
while ((1 << S) < numsets) | |
{ | |
++S; | |
} | |
// Output the parameters | |
*log2_L = L; | |
*log2_A = A; | |
*log2_S = S; | |
} | |
///// | |
ALT_STATUS_CODE alt_cache_l1_enable_all(void) | |
{ | |
alt_cache_l1_disable_all(); | |
// Parity should be turned on before anything else. | |
alt_cache_l1_parity_enable(); | |
alt_cache_l1_instruction_enable(); | |
alt_cache_l1_data_enable(); | |
alt_cache_l1_branch_enable(); | |
alt_cache_l1_prefetch_enable(); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_disable_all(void) | |
{ | |
alt_cache_l1_parity_disable(); | |
alt_cache_l1_instruction_disable(); | |
alt_cache_l1_data_disable(); | |
alt_cache_l1_branch_disable(); | |
alt_cache_l1_prefetch_disable(); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_instruction_enable(void) | |
{ | |
// Update SCTLR.I bit (bit 12) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
if ((sctlr & ALT_CPU_SCTLR_I_SET_MSK) == 0) | |
{ | |
alt_cache_l1_instruction_invalidate(); | |
sctlr |= ALT_CPU_SCTLR_I_SET_MSK; | |
sctlr_write_helper(sctlr); | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_instruction_disable(void) | |
{ | |
// Update SCTLR.I bit (bit 12) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
sctlr &= ~ALT_CPU_SCTLR_I_SET_MSK; | |
sctlr_write_helper(sctlr); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l1_instruction_is_enabled(void) | |
{ | |
// Query SCTLR.I bit (bit 12) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
if ( (sctlr & ALT_CPU_SCTLR_I_SET_MSK) != 0 ) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
ALT_STATUS_CODE alt_cache_l1_instruction_invalidate(void) | |
{ | |
// Issue the ICIALLUIS (Instruction Cache Invalidate ALL to point of | |
// Unification Inner Shareable) cache maintenance operation | |
// See ARMv7-A,R, section B4.2.1. | |
uint32_t dummy = 0; | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, dummy, c7, c1, 0"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c1, 0" : : "r" (dummy)); | |
#endif | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_enable(void) | |
{ | |
// Update SCTLR.C bit (bit 2) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
if ((sctlr & ALT_CPU_SCTLR_C_SET_MSK) == 0) | |
{ | |
alt_cache_l1_data_invalidate_all(); | |
sctlr |= ALT_CPU_SCTLR_C_SET_MSK; | |
sctlr_write_helper(sctlr); | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_disable(void) | |
{ | |
// Update SCTLR.C bit (bit 2) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
if ((sctlr & ALT_CPU_SCTLR_C_SET_MSK) != 0) | |
{ | |
alt_cache_l1_data_clean_all(); | |
sctlr &= ~ALT_CPU_SCTLR_C_SET_MSK; | |
sctlr_write_helper(sctlr); | |
} | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l1_data_is_enabled(void) | |
{ | |
// Query SCTLR.C bit (bit 2) | |
// See Cortex-A9 TRM, section 4.3.9 | |
uint32_t sctlr = sctlr_read_helper(); | |
if ( (sctlr & ALT_CPU_SCTLR_C_SET_MSK) != 0 ) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
static void alt_cache_l1_data_invalidate_helper(void * vaddress, size_t length) | |
{ | |
// Repeatedly call DCIMVAC (Data Cache Invalidate by Modified Virtual | |
// Address to the point of Coherency) and loop for the length of the | |
// segment. | |
// The DCIMVAC uses the MVA format for the register. This is simply the | |
// virtual address of the line to be invalidated. | |
// See ARMv7-A,R, section B4.2.1. | |
for (uintptr_t va = (uintptr_t)vaddress; va < (uintptr_t)vaddress + length; va += ALT_CACHE_LINE_SIZE) | |
{ | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, va, c7, c6, 1"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c6, 1" : : "r" (va)); | |
#endif | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_invalidate(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_cache_l1_data_invalidate_helper(vaddress, length); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_invalidate_all(void) | |
{ | |
// Gather parameters for DCISW (Data Cache Invalidate by Set / Way) data format. | |
// See ARMv7-A,R, section B4.2.1 | |
// Query the log2(line size in bytes), log2(associativity), log2(set count) for the data cache. | |
uint32_t L = 0; | |
uint32_t A = 0; | |
uint32_t S = 0; | |
cssidr_decode_helper(false, &L, &A, &S); | |
// Repeatedly call DCISW and loop for every cache way and set. | |
for (int way = 0; way < (1 << A); ++way) | |
{ | |
for (int set = 0; set < (1 << S); ++set) | |
{ | |
uint32_t way_set_info = 0; | |
way_set_info |= way << (32 - A); | |
way_set_info |= set << (L); | |
// Level is 0 because we're invalidating the L1. | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, way_set_info, c7, c6, 2"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c6, 2" : : "r" (way_set_info)); | |
#endif | |
} | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
return ALT_E_SUCCESS; | |
} | |
static void alt_cache_l1_data_clean_helper(void * vaddress, size_t length) | |
{ | |
// Repeatedly call DCCMVAC (Data Cache Clean by Modified Virtual Address to | |
// point of Coherency) and loop for the length of the segment. | |
// The DCCMVAC uses the MVA format for the register. This is simply the | |
// virtual address of the line to be invalidated. | |
// See ARMv7-A,R, section B4.2.1. | |
for (uintptr_t va = (uintptr_t)vaddress; va < (uintptr_t)vaddress + length; va += ALT_CACHE_LINE_SIZE) | |
{ | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, va, c7, c10, 1"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c10, 1" : : "r" (va)); | |
#endif | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_clean(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_cache_l1_data_clean_helper(vaddress, length); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_clean_all(void) | |
{ | |
// Gather parameters for DCCSW (Data Cache Clean by Set / Way) data format | |
// See ARMv7-A,R, section B4.2.1 | |
// Query the log2(line size in bytes), log2(associativity), log2(set count) for the data cache. | |
uint32_t L = 0; | |
uint32_t A = 0; | |
uint32_t S = 0; | |
cssidr_decode_helper(false, &L, &A, &S); | |
// Repeatedly call DCCSW and loop for every cache way and set. | |
for (int way = 0; way < (1 << A); ++way) | |
{ | |
for (int set = 0; set < (1 << S); ++set) | |
{ | |
uint32_t way_set_info = 0; | |
way_set_info |= way << (32 - A); | |
way_set_info |= set << (L); | |
// Level is 0 because we're invalidating the L1. | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, way_set_info, c7, c10, 2"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c10, 2" : : "r" (way_set_info)); | |
#endif | |
} | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
return ALT_E_SUCCESS; | |
} | |
static void alt_cache_l1_data_purge_helper(void * vaddress, size_t length) | |
{ | |
// Repeatedly call DCCIMVAC (Data Cache Clean and Invalidate by Modified | |
// Virtual Address to point of Coherency) and loop for the length of the | |
// segment. | |
// The DCCIMVAC uses the MVA format for the register. This is simply the | |
// virtual address of the line to be invalidated. | |
// See ARMv7-A,R, section B4.2.1. | |
for (uintptr_t va = (uintptr_t)vaddress; va < (uintptr_t)vaddress + length; va += ALT_CACHE_LINE_SIZE) | |
{ | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, va, c7, c14, 1"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c14, 1" : : "r" (va)); | |
#endif | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_purge(void * vaddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)vaddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_cache_l1_data_purge_helper(vaddress, length); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_data_purge_all(void) | |
{ | |
// Gather parameters for DCCISW (Data Cache Clean and Invalidate by Set / Way) data format | |
// See ARMv7-A,R, section B4.2.1 | |
// Query the log2(line size in bytes), log2(associativity), log2(set count) for the data cache. | |
uint32_t L = 0; | |
uint32_t A = 0; | |
uint32_t S = 0; | |
cssidr_decode_helper(false, &L, &A, &S); | |
// Repeatedly call DCCISW and loop for every cache way and set. | |
for (int way = 0; way < (1 << A); ++way) | |
{ | |
for (int set = 0; set < (1 << S); ++set) | |
{ | |
uint32_t way_set_info = 0; | |
way_set_info |= way << (32 - A); | |
way_set_info |= set << (L); | |
// Level is 0 because we're invalidating the L1. | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, way_set_info, c7, c14, 2"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c14, 2" : : "r" (way_set_info)); | |
#endif | |
} | |
} | |
// Ensure all cache maintenance operations complete before returning. | |
__asm("dsb"); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_parity_enable(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
if ((actlr & ALT_CPU_ACTLR_PARITYON_SET_MSK) == 0) | |
{ | |
// Determine which caches which will be affected by parity being enabled | |
// are currently enabled. | |
bool dcache_en = alt_cache_l1_data_is_enabled(); | |
bool icache_en = alt_cache_l1_instruction_is_enabled(); | |
bool branch_en = alt_cache_l1_branch_is_enabled(); | |
// For those caches, disable them temporarily | |
if (icache_en == true) | |
{ | |
alt_cache_l1_instruction_disable(); | |
} | |
if (dcache_en == true) | |
{ | |
alt_cache_l1_data_disable(); | |
} | |
if (branch_en == true) | |
{ | |
alt_cache_l1_branch_disable(); | |
} | |
// Turn on parity in the L1. | |
actlr |= ALT_CPU_ACTLR_PARITYON_SET_MSK; | |
actlr_write_helper(actlr); | |
// Now enable them again. | |
if (icache_en == true) | |
{ | |
alt_cache_l1_instruction_enable(); | |
} | |
if (dcache_en == true) | |
{ | |
alt_cache_l1_data_enable(); | |
} | |
if (branch_en == true) | |
{ | |
alt_cache_l1_branch_enable(); | |
} | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_parity_disable(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
actlr &= ~ALT_CPU_ACTLR_PARITYON_SET_MSK; | |
actlr_write_helper(actlr); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l1_parity_is_enabled(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
if ((actlr & ALT_CPU_ACTLR_PARITYON_SET_MSK) != 0) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
ALT_STATUS_CODE alt_cache_l1_branch_enable(void) | |
{ | |
alt_cache_l1_branch_invalidate(); | |
uint32_t sctlr = sctlr_read_helper(); | |
sctlr |= ALT_CPU_SCTLR_Z_SET_MSK; | |
sctlr_write_helper(sctlr); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_branch_disable(void) | |
{ | |
uint32_t sctlr = sctlr_read_helper(); | |
sctlr &= ~ALT_CPU_SCTLR_Z_SET_MSK; | |
sctlr_write_helper(sctlr); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_branch_invalidate(void) | |
{ | |
// Issue BPIALLIS (Branch Predictor Invalidate ALL, Inner Shareable). | |
uint32_t dummy = 0; | |
#ifdef __ARMCC_VERSION | |
__asm("MCR p15, 0, dummy, c7, c1, 6"); | |
#else | |
__asm("MCR p15, 0, %0, c7, c1, 6" : : "r" (dummy)); | |
#endif | |
// Ensure all branch predictor maintenance operations complete before returning. | |
__asm("dsb"); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l1_branch_is_enabled(void) | |
{ | |
uint32_t sctlr = sctlr_read_helper(); | |
if ((sctlr & ALT_CPU_SCTLR_Z_SET_MSK) != 0) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
ALT_STATUS_CODE alt_cache_l1_prefetch_enable(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
actlr |= ALT_CPU_ACTLR_L1PREFETCHEN_SET_MSK; | |
actlr_write_helper(actlr); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l1_prefetch_disable(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
actlr &= ~ALT_CPU_ACTLR_L1PREFETCHEN_SET_MSK; | |
actlr_write_helper(actlr); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l1_prefetch_is_enabled(void) | |
{ | |
uint32_t actlr = actlr_read_helper(); | |
if ((actlr & ALT_CPU_ACTLR_L1PREFETCHEN_SET_MSK) != 0) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
///// | |
// | |
// Stand-in SoCAL for MPU L2 registers. | |
// | |
// All offsets are from ALT_MPUL2_OFST. | |
// | |
#define ALT_MPUL2_CACHE_ID_OFST 0x000 | |
#define ALT_MPUL2_CACHE_TYPE_OFST 0x004 | |
#define ALT_MPUL2_CONTROL_OFST 0x100 | |
#define ALT_MPUL2_AUX_CONTROL_OFST 0x104 | |
#define ALT_MPUL2_TAG_RAM_CONTROL_OFST 0x108 | |
#define ALT_MPUL2_DATA_RAM_CONTROL_OFST 0x10c | |
#define ALT_MPUL2_EV_COUNTER_CTRL_OFST 0x200 | |
#define ALT_MPUL2_EV_COUNTER1_CFG_OFST 0x204 | |
#define ALT_MPUL2_EV_COUNTER0_CFG_OFST 0x208 | |
#define ALT_MPUL2_EV_COUNTER1_OFST 0x20c | |
#define ALT_MPUL2_EV_COUNTER0_OFST 0x210 | |
#define ALT_MPUL2_INT_MASK_OFST 0x214 | |
#define ALT_MPUL2_INT_MASK_STATUS_OFST 0x218 | |
#define ALT_MPUL2_INT_RAW_STATUS_OFST 0x21c | |
#define ALT_MPUL2_INT_CLEAR_OFST 0x220 | |
#define ALT_MPUL2_CACHE_SYNC_OFST 0x730 | |
#define ALT_MPUL2_INV_PA_OFST 0x770 | |
#define ALT_MPUL2_INV_WAY_OFST 0x77c | |
#define ALT_MPUL2_CLEAN_PA_OFST 0x7b0 | |
#define ALT_MPUL2_CLEAN_INDEX_OFST 0x7b8 | |
#define ALT_MPUL2_CLEAN_WAY_OFST 0x7bc | |
#define ALT_MPUL2_CLEAN_INV_PA_OFST 0x7f0 | |
#define ALT_MPUL2_CLEAN_INV_INDEX_OFST 0x7f8 | |
#define ALT_MPUL2_CLEAN_INV_WAY_OFST 0x7fc | |
#define ALT_MPUL2_D_LOCKDOWN0_OFST 0x900 | |
#define ALT_MPUL2_I_LOCKDOWN0_OFST 0x904 | |
#define ALT_MPUL2_D_LOCKDOWN1_OFST 0x908 | |
#define ALT_MPUL2_I_LOCKDOWN1_OFST 0x90c | |
#define ALT_MPUL2_D_LOCKDOWN2_OFST 0x910 | |
#define ALT_MPUL2_I_LOCKDOWN2_OFST 0x914 | |
#define ALT_MPUL2_D_LOCKDOWN3_OFST 0x918 | |
#define ALT_MPUL2_I_LOCKDOWN3_OFST 0x91c | |
#define ALT_MPUL2_D_LOCKDOWN4_OFST 0x920 | |
#define ALT_MPUL2_I_LOCKDOWN4_OFST 0x924 | |
#define ALT_MPUL2_D_LOCKDOWN5_OFST 0x928 | |
#define ALT_MPUL2_I_LOCKDOWN5_OFST 0x92c | |
#define ALT_MPUL2_D_LOCKDOWN6_OFST 0x930 | |
#define ALT_MPUL2_I_LOCKDOWN6_OFST 0x934 | |
#define ALT_MPUL2_D_LOCKDOWN7_OFST 0x938 | |
#define ALT_MPUL2_I_LOCKDOWN7_OFST 0x93c | |
#define ALT_MPUL2_D_LOCKDOWNx_OFST(x) (0x900 + ((x) * 0x8)) | |
#define ALT_MPUL2_I_LOCKDOWNx_OFST(x) (0x904 + ((x) * 0x8)) | |
#define ALT_MPUL2_LOCK_LINE_EN_OFST 0x950 | |
#define ALT_MPUL2_UNLOCK_WAY_OFST 0x954 | |
#define ALT_MPUL2_ADDR_FILTERING_START_OFST 0xc00 | |
#define ALT_MPUL2_ADDR_FILTERING_END_OFST 0xc04 | |
#define ALT_MPUL2_DEBUG_CTRL_OFST 0xf40 | |
#define ALT_MPUL2_PREFETCH_CTRL_OFST 0xf60 | |
#define ALT_MPUL2_POWER_CTRL_OFST 0xf80 | |
#define ALT_MPUL2_CACHE_ID_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CACHE_ID_OFST)) | |
#define ALT_MPUL2_CACHE_TYPE_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CACHE_TYPE_OFST)) | |
#define ALT_MPUL2_CONTROL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CONTROL_OFST)) | |
#define ALT_MPUL2_AUX_CONTROL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_AUX_CONTROL_OFST)) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_TAG_RAM_CONTROL_OFST)) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_DATA_RAM_CONTROL_OFST)) | |
#define ALT_MPUL2_EV_COUNTER_CTRL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_EV_COUNTER_CTRL_OFST)) | |
#define ALT_MPUL2_EV_COUNTER1_CFG_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_EV_COUNTER1_CFG_OFST)) | |
#define ALT_MPUL2_EV_COUNTER0_CFG_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_EV_COUNTER0_CFG_OFST)) | |
#define ALT_MPUL2_EV_COUNTER1_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_EV_COUNTER1_OFST)) | |
#define ALT_MPUL2_EV_COUNTER0_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_EV_COUNTER0_OFST)) | |
#define ALT_MPUL2_INT_MASK_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INT_MASK_OFST)) | |
#define ALT_MPUL2_INT_MASK_STATUS_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INT_MASK_STATUS_OFST)) | |
#define ALT_MPUL2_INT_RAW_STATUS_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INT_RAW_STATUS_OFST)) | |
#define ALT_MPUL2_INT_CLEAR_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INT_CLEAR_OFST)) | |
#define ALT_MPUL2_CACHE_SYNC_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CACHE_SYNC_OFST)) | |
#define ALT_MPUL2_INV_PA_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INV_PA_OFST)) | |
#define ALT_MPUL2_INV_WAY_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_INV_WAY_OFST)) | |
#define ALT_MPUL2_CLEAN_PA_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_PA_OFST)) | |
#define ALT_MPUL2_CLEAN_INDEX_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_INDEX_OFST)) | |
#define ALT_MPUL2_CLEAN_WAY_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_WAY_OFST)) | |
#define ALT_MPUL2_CLEAN_INV_PA_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_INV_PA_OFST)) | |
#define ALT_MPUL2_CLEAN_INV_INDEX_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_INV_INDEX_OFST)) | |
#define ALT_MPUL2_CLEAN_INV_WAY_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_CLEAN_INV_WAY_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN0_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN0_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN0_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN0_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN1_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN1_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN1_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN1_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN2_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN2_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN2_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN2_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN3_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN3_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN3_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN3_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN4_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN4_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN4_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN4_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN5_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN5_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN5_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN5_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN6_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN6_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN6_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN6_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWN7_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWN7_OFST)) | |
#define ALT_MPUL2_I_LOCKDOWN7_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWN7_OFST)) | |
#define ALT_MPUL2_D_LOCKDOWNx_ADDR(x) ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_D_LOCKDOWNx_OFST(x))) | |
#define ALT_MPUL2_I_LOCKDOWNx_ADDR(x) ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_I_LOCKDOWNx_OFST(x))) | |
#define ALT_MPUL2_LOCK_LINE_EN_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_LOCK_LINE_EN_OFST)) | |
#define ALT_MPUL2_UNLOCK_WAY_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_UNLOCK_WAY_OFST)) | |
#define ALT_MPUL2_ADDR_FILTERING_START_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_ADDR_FILTERING_START_OFST)) | |
#define ALT_MPUL2_ADDR_FILTERING_END_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_ADDR_FILTERING_END_OFST)) | |
#define ALT_MPUL2_DEBUG_CTRL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_DEBUG_CTRL_OFST)) | |
#define ALT_MPUL2_PREFETCH_CTRL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_PREFETCH_CTRL_OFST)) | |
#define ALT_MPUL2_POWER_CTRL_ADDR ALT_CAST(void *, (ALT_CAST(char *, ALT_MPUL2_ADDR) + ALT_MPUL2_POWER_CTRL_OFST)) | |
#define ALT_MPUL2_CONTROL_EN_SET_MSK (1 << 0) | |
#define ALT_MPUL2_AUX_CONTROL_PARITY_EN_SET_MSK (1 << 21) | |
#define ALT_MPUL2_AUX_CONTROL_WAYSIZE_SET_MSK (7 << 17) | |
#define ALT_MPUL2_AUX_CONTROL_ASSOCIATIVITY_SET_MSK (1 << 16) | |
#define ALT_MPUL2_AUX_CONTROL_WAYSIZE_VALUE_GET(value) (((value) >> 17) & 0x7) | |
#define ALT_MPUL2_AUX_CONTROL_FULLLINEOFZERO_EN_SET_MSK (1 << 0) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_WRITE_LATENCY_VALUE_GET(value) (((value) >> 8) & 0x7) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_WRITE_LATENCY_VALUE_SET(value) (((value) & 0x7) << 8) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_READ_LATENCY_VALUE_GET(value) (((value) >> 4) & 0x7) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_READ_LATENCY_VALUE_SET(value) (((value) & 0x7) << 4) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_SETUP_LATENCY_VALUE_GET(value) (((value) >> 0) & 0x7) | |
#define ALT_MPUL2_TAG_RAM_CONTROL_SETUP_LATENCY_VALUE_SET(value) (((value) & 0x7) << 0) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_WRITE_LATENCY_VALUE_GET(value) (((value) >> 8) & 0x7) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_WRITE_LATENCY_VALUE_SET(value) (((value) & 0x7) << 8) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_READ_LATENCY_VALUE_GET(value) (((value) >> 4) & 0x7) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_READ_LATENCY_VALUE_SET(value) (((value) & 0x7) << 4) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_SETUP_LATENCY_VALUE_GET(value) (((value) >> 0) & 0x7) | |
#define ALT_MPUL2_DATA_RAM_CONTROL_SETUP_LATENCY_VALUE_SET(value) (((value) & 0x7) << 0) | |
#define ALT_MPUL2_PREFETCH_CTRL_I_PF_EN_SET_MSK (1 << 29) | |
#define ALT_MPUL2_PREFETCH_CTRL_D_PF_EN_SET_MSK (1 << 28) | |
///// | |
#define ALT_CACHE_L2_INTERRUPT_ALL (ALT_CACHE_L2_INTERRUPT_DECERR | \ | |
ALT_CACHE_L2_INTERRUPT_SLVERR | \ | |
ALT_CACHE_L2_INTERRUPT_ERRRD | \ | |
ALT_CACHE_L2_INTERRUPT_ERRRT | \ | |
ALT_CACHE_L2_INTERRUPT_ERRWD | \ | |
ALT_CACHE_L2_INTERRUPT_ERRWT | \ | |
ALT_CACHE_L2_INTERRUPT_PARRD | \ | |
ALT_CACHE_L2_INTERRUPT_PARRT | \ | |
ALT_CACHE_L2_INTERRUPT_ECNTR) | |
///// | |
static uint32_t alt_cache_l2_waymask = 0x0000ffff; | |
ALT_STATUS_CODE alt_cache_l2_init(void) | |
{ | |
// Query the cache characteristics | |
uint32_t auxctrl = alt_read_word(ALT_MPUL2_AUX_CONTROL_ADDR); | |
if (auxctrl & ALT_MPUL2_AUX_CONTROL_ASSOCIATIVITY_SET_MSK) | |
{ | |
alt_cache_l2_waymask = 0x0000ffff; | |
} | |
else | |
{ | |
alt_cache_l2_waymask = 0x000000ff; | |
} | |
///// | |
// Initialize the L2CC using instructions from L2C-310, section 3.1.1. | |
// Write global configuration: | |
// - Associativity, way size | |
// - Latencies for RAM accesses | |
// - Allocation policy | |
// - Prefetch and power capability | |
// (Not needed as the associated lines are by default set to the instantiation parameters) | |
// Invalidate by way all cache entries | |
// (Not needed as it will be invalidated when L2 is enabled) | |
alt_write_word(ALT_MPUL2_TAG_RAM_CONTROL_ADDR, | |
ALT_MPUL2_TAG_RAM_CONTROL_WRITE_LATENCY_VALUE_SET(0) | |
| ALT_MPUL2_TAG_RAM_CONTROL_READ_LATENCY_VALUE_SET(0) | |
| ALT_MPUL2_TAG_RAM_CONTROL_SETUP_LATENCY_VALUE_SET(0)); | |
alt_write_word(ALT_MPUL2_DATA_RAM_CONTROL_ADDR, | |
ALT_MPUL2_DATA_RAM_CONTROL_WRITE_LATENCY_VALUE_SET(0) | |
| ALT_MPUL2_DATA_RAM_CONTROL_READ_LATENCY_VALUE_SET(1) | |
| ALT_MPUL2_DATA_RAM_CONTROL_SETUP_LATENCY_VALUE_SET(0)); | |
// Clear interrupts just in case. | |
alt_cache_l2_int_status_clear(ALT_CACHE_L2_INTERRUPT_ALL); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_uninit(void) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
#define ALT_MPUL2_PREFETCH_CTRL_PF_EN_SET_MSK \ | |
(ALT_MPUL2_PREFETCH_CTRL_I_PF_EN_SET_MSK | ALT_MPUL2_PREFETCH_CTRL_D_PF_EN_SET_MSK) | |
ALT_STATUS_CODE alt_cache_l2_prefetch_enable(void) | |
{ | |
// Use the Prefetch Control Register instead of Aux Control. This is | |
// because the Prefetch Control can be changed while the L2 is enabled. | |
// For more information, see L2C-310, section 3.3.14. | |
alt_setbits_word(ALT_MPUL2_PREFETCH_CTRL_ADDR, ALT_MPUL2_PREFETCH_CTRL_PF_EN_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_prefetch_disable(void) | |
{ | |
// Use the Prefetch Control Register instead of Aux Control. This is | |
// because the Prefetch Control can be changed while the L2 is enabled. | |
// For more information, see L2C-310, section 3.3.14. | |
alt_clrbits_word(ALT_MPUL2_PREFETCH_CTRL_ADDR, ALT_MPUL2_PREFETCH_CTRL_PF_EN_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l2_prefetch_is_enabled(void) | |
{ | |
// Query the Prefetch Control Register. | |
// For more information, see L2C-310, section 3.3.14. | |
uint32_t pfctrl = alt_read_word(ALT_MPUL2_PREFETCH_CTRL_ADDR); | |
if ((pfctrl & ALT_MPUL2_PREFETCH_CTRL_PF_EN_SET_MSK) == ALT_MPUL2_PREFETCH_CTRL_PF_EN_SET_MSK) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
ALT_STATUS_CODE alt_cache_l2_parity_enable(void) | |
{ | |
bool l2_enabled = alt_cache_l2_is_enabled(); | |
if (l2_enabled) | |
{ | |
alt_cache_l2_disable(); | |
} | |
alt_setbits_word(ALT_MPUL2_AUX_CONTROL_ADDR, ALT_MPUL2_AUX_CONTROL_PARITY_EN_SET_MSK); | |
if (l2_enabled) | |
{ | |
alt_cache_l2_enable(); | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_parity_disable(void) | |
{ | |
alt_clrbits_word(ALT_MPUL2_AUX_CONTROL_ADDR, ALT_MPUL2_AUX_CONTROL_PARITY_EN_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l2_parity_is_enabled(void) | |
{ | |
uint32_t auxctrl = alt_read_word(ALT_MPUL2_AUX_CONTROL_ADDR); | |
if ((auxctrl & ALT_MPUL2_AUX_CONTROL_PARITY_EN_SET_MSK) == ALT_MPUL2_AUX_CONTROL_PARITY_EN_SET_MSK) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
ALT_STATUS_CODE alt_cache_l2_enable(void) | |
{ | |
if (!alt_cache_l2_is_enabled()) | |
{ | |
alt_cache_l2_invalidate_all(); | |
alt_write_word(ALT_MPUL2_CONTROL_ADDR, ALT_MPUL2_CONTROL_EN_SET_MSK); | |
} | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_disable(void) | |
{ | |
if (alt_cache_l2_is_enabled()) | |
{ | |
alt_cache_l2_purge_all(); | |
alt_write_word(ALT_MPUL2_CONTROL_ADDR, 0); | |
} | |
return ALT_E_SUCCESS; | |
} | |
bool alt_cache_l2_is_enabled(void) | |
{ | |
uint32_t ctrl = alt_read_word(ALT_MPUL2_CONTROL_ADDR); | |
if ((ctrl & ALT_MPUL2_CONTROL_EN_SET_MSK) != 0) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
// Common cache maintenance operation register formats | |
// | |
#define ALT_MPUL2_COMMON_PA_TAG_SET_MSK (0xfffff << 12) | |
#define ALT_MPUL2_COMMON_PA_INDEX_SET_MSK (0x7f << 5) | |
#define ALT_MPUL2_COMMON_PA_C_SET_MSK (0x1 << 0) | |
#define ALT_MPUL2_COMMON_INDEXWAY_WAY_SET_MSK (0xf << 28) | |
#define ALT_MPUL2_COMMON_INDEXWAY_INDEX_SET_MSK (0x7f << 5) | |
#define ALT_MPUL2_COMMON_INDEXWAY_C_SET_MSK (0x1 << 0) | |
#define ALT_MPUL2_COMMON_SYNC_C_SET_MSK (1 << 0) | |
#define ALT_MPUL2_COMMON_WAY_WAY_SET_MSK (0xffff << 0) | |
///// | |
// Timeouts for various L2 cache maintenance operations. | |
// The ranges of the operations can be determined by enabling debug printing. | |
#define ALT_CACHE_L2_SYNC_TIMEOUT 128 | |
#define ALT_CACHE_L2_INVALIDATE_ALL_TIMEOUT 4096 | |
#define ALT_CACHE_L2_CLEAN_ALL_TIMEOUT 65536 | |
#define ALT_CACHE_L2_PURGE_ALL_TIMEOUT 65536 | |
#define ALT_CACHE_L2_INVALIDATE_ADDR_TIMEOUT 128 | |
#define ALT_CACHE_L2_CLEAN_ADDR_TIMEOUT 128 | |
#define ALT_CACHE_L2_PURGE_ADDR_TIMEOUT 128 | |
ALT_STATUS_CODE alt_cache_l2_sync(void) | |
{ | |
// Issue cache sync command, then wait for it to complete by polling the same register. | |
// For more information, see L2C-310, section 3.3.10. | |
alt_write_word(ALT_MPUL2_CACHE_SYNC_ADDR, 0); | |
int i = 0; | |
while (alt_read_word(ALT_MPUL2_CACHE_SYNC_ADDR)) | |
{ | |
// Atomic operation still in progress. | |
if (i == ALT_CACHE_L2_SYNC_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Sync time = %d.\n", i); | |
return ALT_E_SUCCESS; | |
} | |
static ALT_STATUS_CODE alt_cache_l2_invalidate_helper(uintptr_t paddress, size_t length) | |
{ | |
int i = 0; | |
int seg = 0; | |
// For each stride: Issue invalidate line by PA command, then wait for it | |
// to complete by polling the same register. | |
// For more information, see L2C-310, section 3.3.10. | |
for (uintptr_t pa = paddress; pa < paddress + length; pa += ALT_CACHE_LINE_SIZE) | |
{ | |
alt_write_word(ALT_MPUL2_INV_PA_ADDR, pa); | |
++seg; | |
} | |
while (alt_read_word(ALT_MPUL2_INV_PA_ADDR)) | |
{ | |
// Atomic operation still in progress. | |
if (i == ALT_CACHE_L2_INVALIDATE_ADDR_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Invalidate PA time = %d for %d segment(s)\n", i, seg); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_invalidate(void * paddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)paddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
return alt_cache_l2_invalidate_helper((uintptr_t)paddress, length); | |
} | |
ALT_STATUS_CODE alt_cache_l2_invalidate_all(void) | |
{ | |
// Invalidate by way, all ways. | |
// For more information, see L2C-310, section 3.3.10. | |
alt_write_word(ALT_MPUL2_INV_WAY_ADDR, alt_cache_l2_waymask); | |
int i = 0; | |
while (alt_read_word(ALT_MPUL2_INV_WAY_ADDR)) | |
{ | |
// Background operation still in progress. | |
if (i == ALT_CACHE_L2_INVALIDATE_ALL_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Invalidate All time = %d.\n", i); | |
return ALT_E_SUCCESS; | |
} | |
static ALT_STATUS_CODE alt_cache_l2_clean_helper(uintptr_t paddress, size_t length) | |
{ | |
int i = 0; | |
int seg = 0; | |
// For each stride: Issue clean line by PA command, then wait for it to | |
// complete by polling the same register. | |
// For more information, see L2C-310, section 3.3.10. | |
for (uintptr_t pa = paddress; pa < paddress + length; pa += ALT_CACHE_LINE_SIZE) | |
{ | |
alt_write_word(ALT_MPUL2_CLEAN_PA_ADDR, pa); | |
++seg; | |
} | |
while (alt_read_word(ALT_MPUL2_CLEAN_PA_ADDR) & ALT_MPUL2_COMMON_PA_C_SET_MSK) | |
{ | |
// Atomic operation still in progress. | |
if (i == ALT_CACHE_L2_CLEAN_ADDR_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Clean PA time = %d for %d segment(s)\n", i, seg); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_clean(void * paddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)paddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
return alt_cache_l2_clean_helper((uintptr_t)paddress, length); | |
} | |
ALT_STATUS_CODE alt_cache_l2_clean_all(void) | |
{ | |
// Clean by way, all ways. | |
// For more information, see L2C-310, section 3.3.10. | |
alt_write_word(ALT_MPUL2_CLEAN_WAY_ADDR, alt_cache_l2_waymask); | |
int i = 0; | |
while (alt_read_word(ALT_MPUL2_CLEAN_WAY_ADDR)) | |
{ | |
// Background operation still in progress. | |
if (i == ALT_CACHE_L2_CLEAN_ALL_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Invalidate All time = %d.\n", i); | |
return ALT_E_SUCCESS; | |
} | |
static ALT_STATUS_CODE alt_cache_l2_purge_helper(uintptr_t paddress, size_t length) | |
{ | |
int i = 0; | |
int seg = 0; | |
// For each stride: Issue clean and invalidate line by PA command, then | |
// wait for it to complete by polling the same register. | |
// For more information, see L2C-310, section 3.3.10. | |
for (uintptr_t pa = paddress; pa < paddress + length; pa += ALT_CACHE_LINE_SIZE) | |
{ | |
alt_write_word(ALT_MPUL2_CLEAN_INV_PA_ADDR, pa); | |
++seg; | |
} | |
while (alt_read_word(ALT_MPUL2_CLEAN_INV_PA_ADDR) & ALT_MPUL2_COMMON_PA_C_SET_MSK) | |
{ | |
// Atomic operation still in progress. | |
if (i == ALT_CACHE_L2_PURGE_ADDR_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Purge PA time = %d for %d segment(s)\n", i, seg); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_purge(void * paddress, size_t length) | |
{ | |
// Verify preconditions: | |
// - length is non-zero | |
// - address and length are on the cache boundaries | |
if (length == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (((uintptr_t)paddress & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if ((length & (ALT_CACHE_LINE_SIZE - 1)) != 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
return alt_cache_l2_purge_helper((uintptr_t)paddress, length); | |
} | |
ALT_STATUS_CODE alt_cache_l2_purge_all(void) | |
{ | |
// Clean and invalidate by way, all ways. | |
// For more information, see L2C-310, section 3.3.10. | |
alt_write_word(ALT_MPUL2_CLEAN_INV_WAY_ADDR, alt_cache_l2_waymask); | |
int i = 0; | |
while (alt_read_word(ALT_MPUL2_CLEAN_INV_WAY_ADDR)) | |
{ | |
// Background operation still in progress. | |
if (i == ALT_CACHE_L2_PURGE_ALL_TIMEOUT) | |
{ | |
return ALT_E_TMO; | |
} | |
++i; | |
} | |
dprintf("DEBUG[cache][l2]: L2 Purge All time = %d.\n", i); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_int_enable(uint32_t interrupt) | |
{ | |
// Validate the interrupt mask | |
if ((interrupt & ALT_CACHE_L2_INTERRUPT_ALL) == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_setbits_word(ALT_MPUL2_INT_MASK_ADDR, interrupt); | |
return ALT_E_SUCCESS; | |
} | |
ALT_STATUS_CODE alt_cache_l2_int_disable(uint32_t interrupt) | |
{ | |
// Validate the interrupt mask | |
if ((interrupt & ALT_CACHE_L2_INTERRUPT_ALL) == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_clrbits_word(ALT_MPUL2_INT_MASK_ADDR, interrupt); | |
return ALT_E_SUCCESS; | |
} | |
uint32_t alt_cache_l2_int_status_get(void) | |
{ | |
return alt_read_word(ALT_MPUL2_INT_MASK_STATUS_ADDR); | |
} | |
ALT_STATUS_CODE alt_cache_l2_int_status_clear(uint32_t interrupt) | |
{ | |
// Validate the interrupt mask | |
if ((interrupt & ALT_CACHE_L2_INTERRUPT_ALL) == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
alt_write_word(ALT_MPUL2_INT_CLEAR_ADDR, interrupt); | |
return ALT_E_SUCCESS; | |
} | |
///// | |
__attribute__((weak)) ALT_STATUS_CODE alt_int_dist_pending_clear(ALT_INT_INTERRUPT_t int_id) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
inline static uint32_t get_current_cpu_num(void) | |
{ | |
uint32_t affinity; | |
// Use the MPIDR. This is only available at PL1 or higher. | |
// See ARMv7, section B4.1.106. | |
#ifdef __ARMCC_VERSION | |
__asm("MRC p15, 0, affinity, c0, c0, 5"); | |
#else | |
__asm("MRC p15, 0, %0, c0, c0, 5" : "=r" (affinity)); | |
#endif | |
return affinity & 0xFF; | |
} | |
ALT_STATUS_CODE alt_cache_l2_ecc_start(void * block, size_t size) | |
{ | |
uint32_t way_size = (8 * 1024) << ALT_MPUL2_AUX_CONTROL_WAYSIZE_VALUE_GET(alt_read_word(ALT_MPUL2_AUX_CONTROL_ADDR)); | |
// Add 32 KiB to the scrubbing size to account for effects of the L1 on scrubbing. | |
uint32_t scrub_way_size = way_size + (32 * 1024); | |
if (size < scrub_way_size) | |
{ | |
return ALT_E_ERROR; | |
} | |
if (alt_cache_l2_is_enabled() == false) | |
{ | |
return ALT_E_ERROR; | |
} | |
///// | |
bool l1_icache = false; | |
bool l1_prefetch = false; | |
bool l2_prefetch = false; | |
if (alt_cache_l1_instruction_is_enabled() == true) | |
{ | |
l1_icache = true; | |
alt_cache_l1_instruction_disable(); | |
} | |
if (alt_cache_l1_prefetch_is_enabled() == true) | |
{ | |
l1_prefetch = true; | |
alt_cache_l1_prefetch_disable(); | |
} | |
if (alt_cache_l2_prefetch_is_enabled() == true) | |
{ | |
l2_prefetch = true; | |
alt_cache_l2_prefetch_disable(); | |
} | |
// inline'ed alt_cache_l2_disable(); // This will invalidate all L2 entries | |
alt_cache_l2_purge_all(); | |
alt_write_word(ALT_MPUL2_CONTROL_ADDR, 0); | |
// Enable "Full line of zero" feature of the L2C. | |
// See L2C-310, section 2.5.5, "Full line of zero write". | |
alt_setbits_word(ALT_MPUL2_AUX_CONTROL_ADDR, ALT_MPUL2_AUX_CONTROL_FULLLINEOFZERO_EN_SET_MSK); | |
// ECC should be enabled before L2 is enabled. (NPP MPU, section 4.3, item 1) | |
alt_write_word(ALT_SYSMGR_ECC_L2_ADDR, ALT_SYSMGR_ECC_L2_EN_SET_MSK); | |
// inline'ed alt_cache_l2_enable(); | |
// No need to invalidate all; the previous L2 disable should have purged everything. | |
alt_write_word(ALT_MPUL2_CONTROL_ADDR, ALT_MPUL2_CONTROL_EN_SET_MSK); | |
// Enable "Full line of zero" feature of the A9. | |
// See Cortex-A9 TRM, section 4.3.10. | |
actlr_write_helper(actlr_read_helper() | ALT_CPU_ACTLR_WRITEFULLLINEZEROS_SET_MSK); | |
///// | |
uint32_t cpu_affinity = get_current_cpu_num(); | |
// Loop through all ways for the lock by master configuration. | |
int way_count = (alt_cache_l2_waymask == 0x0000ffff) ? 16 : 8; | |
for (int way_lock = 0; way_lock < way_count; ++way_lock) | |
{ | |
// Lock the current way for Data and Instruction. | |
alt_write_word(ALT_MPUL2_D_LOCKDOWNx_ADDR(cpu_affinity), ~(1 << way_lock) & alt_cache_l2_waymask); | |
alt_write_word(ALT_MPUL2_I_LOCKDOWNx_ADDR(cpu_affinity), ~(1 << way_lock) & alt_cache_l2_waymask); | |
// Invalidate All. This will ensure that scrubbing RAM contents does not exist in a different way. | |
alt_cache_l2_invalidate_all(); | |
// Loop through all words in the block | |
register uint32_t * block2 = block; | |
for (register int i = 0; i < (scrub_way_size / sizeof(*block2)); i++) | |
{ | |
*block2 = 0; | |
++block2; | |
} | |
// Don't put the DSB inside the loop. It will disallow the [full line of zero] optimization. | |
__asm("dsb"); | |
} | |
// Unlock the way lock by master for the current CPU. | |
alt_write_word(ALT_MPUL2_D_LOCKDOWNx_ADDR(cpu_affinity), 0); | |
alt_write_word(ALT_MPUL2_I_LOCKDOWNx_ADDR(cpu_affinity), 0); | |
if (l1_icache) | |
{ | |
alt_cache_l1_instruction_enable(); | |
} | |
if (l1_prefetch) | |
{ | |
alt_cache_l1_prefetch_enable(); | |
} | |
if (l2_prefetch) | |
{ | |
alt_cache_l2_prefetch_enable(); | |
} | |
alt_int_dist_pending_clear(ALT_INT_INTERRUPT_L2_ECC_BYTE_WR_IRQ); | |
alt_int_dist_pending_clear(ALT_INT_INTERRUPT_L2_ECC_CORRECTED_IRQ); | |
alt_int_dist_pending_clear(ALT_INT_INTERRUPT_L2_ECC_UNCORRECTED_IRQ); | |
return ALT_E_SUCCESS; | |
} |