/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2015, Atmel 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: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \file */ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "chip.h" | |
#include "peripherals/l2cc.h" | |
#include "cortex-a/cp15.h" | |
#include "trace.h" | |
#include <assert.h> | |
#include <stdbool.h> | |
#include <string.h> | |
/*---------------------------------------------------------------------------- | |
* Functions | |
*----------------------------------------------------------------------------*/ | |
uint32_t l2cc_is_enabled(void) | |
{ | |
return ((L2CC->L2CC_CR) & L2CC_CR_L2CEN); | |
} | |
void l2cc_enable(void) | |
{ | |
L2CC->L2CC_CR |= L2CC_CR_L2CEN; | |
asm volatile("": : :"memory"); | |
asm("dsb"); | |
asm("isb"); | |
trace_info("L2 cache is enabled\r\n"); | |
} | |
void l2cc_disable(void) | |
{ | |
L2CC->L2CC_CR &= ~L2CC_CR_L2CEN; | |
asm volatile("": : :"memory"); | |
asm("dsb"); | |
asm("isb"); | |
trace_info("L2 cache is disabled\r\n"); | |
} | |
void l2cc_exclusive_cache(uint8_t enable) | |
{ | |
uint32_t cfg; | |
if (l2cc_is_enabled()) { | |
l2cc_disable(); | |
} | |
cfg = L2CC->L2CC_ACR; | |
if (enable) { | |
cp15_exclusive_cache(); | |
cfg |= L2CC_ACR_EXCC; | |
trace_info("L2 Exclusive mode enabled\n\r"); | |
} else { | |
cp15_non_exclusive_cache(); | |
cfg &= ~L2CC_ACR_EXCC; | |
trace_info("L2 Exclusive mode disabled\n\r"); | |
} | |
L2CC->L2CC_ACR |= cfg; | |
} | |
void l2cc_config_lat_ram(struct _ram_latency_control * latencies) | |
{ | |
if (l2cc_is_enabled()) { | |
l2cc_disable(); | |
} | |
L2CC->L2CC_TRCR = | |
(L2CC_TRCR_TSETLAT(latencies->tag.setup) | | |
L2CC_TRCR_TRDLAT(latencies->tag.read) | | |
L2CC_TRCR_TWRLAT(latencies->tag.write)); | |
L2CC->L2CC_DRCR = | |
(L2CC_DRCR_DSETLAT(latencies->data.setup) | | |
L2CC_DRCR_DRDLAT(latencies->data.read) | | |
L2CC_DRCR_DWRLAT(latencies->data.write)); | |
} | |
void l2cc_set_config(const struct _l2cc_control* cfg) | |
{ | |
uint32_t aux_control, debug_control, prefetch_control, power_control; | |
if (cfg->offset > 31) { | |
assert(0); | |
} | |
if ((cfg->offset > 7) && (cfg->offset < 15)) { | |
assert(0); | |
} | |
if ((cfg->offset > 15) && (cfg->offset < 23)) { | |
assert(0); | |
} | |
if ((cfg->offset > 23) && (cfg->offset < 31)) { | |
assert(0); | |
} | |
if (l2cc_is_enabled()) { | |
l2cc_disable(); | |
} | |
aux_control = ((cfg->high_prior_so << 10) | | |
(cfg->store_buff_dev_limit << 11) | | |
(cfg->shared_attr_invalidate << 13) | | |
(cfg->evt_mon_bus << 20) | | |
(cfg->parity << 21) | | |
(cfg->shared_attr_override << 22) | | |
(L2CC_ACR_FWA(cfg->force_write_alloc)) | | |
(cfg->cache_replacement << 25) | | |
(cfg->non_sec_lockdown << 26) | | |
(cfg->it_acces_non_sec << 27) | | |
(cfg->data_prefetch << 28) | | |
(cfg->instruct_prefetch << 29)); | |
debug_control = ((cfg->no_cache_linefill << 0) | | |
(cfg->no_write_back << 1)); | |
prefetch_control = ((L2CC_PCR_OFFSET(cfg->offset << 0)) | | |
(cfg->exclusive_seq_same_id << 21) | | |
(cfg->incr_double_linefill << 23) | | |
(cfg->prefetch_drop << 24) | | |
(cfg->DLFWRDIS << 27) | | |
(cfg->data_prefetch << 28) | | |
(cfg->instruct_prefetch << 29) | | |
(cfg->double_linefill << 30)); | |
power_control = ((cfg->standby_mode << 0) | | |
(cfg->dyn_clock_gating << 1)); | |
L2CC->L2CC_ACR = aux_control; | |
L2CC->L2CC_DCR = debug_control; | |
L2CC->L2CC_PCR = prefetch_control; | |
L2CC->L2CC_POWCR = power_control; | |
} | |
void l2cc_data_prefetch_enable(void) | |
{ | |
L2CC->L2CC_PCR |= L2CC_PCR_DATPEN; | |
} | |
void l2cc_inst_prefetch_enable(void) | |
{ | |
L2CC->L2CC_PCR |= L2CC_PCR_INSPEN; | |
} | |
void l2cc_enable_reset_counter(uint8_t event_counter) | |
{ | |
assert((event_counter > 3) ? 0 : 1); | |
L2CC->L2CC_ECR = (L2CC_ECR_EVCEN | (event_counter << 1)); | |
} | |
void l2cc_event_config(uint8_t event_counter, uint8_t source, uint8_t it) | |
{ | |
if (l2cc_is_enabled()) { | |
L2CC->L2CC_CR = false; | |
} | |
assert((event_counter > 1) ? 0 : 1); | |
if (!event_counter) { | |
L2CC->L2CC_ECFGR0 = (source | it); | |
} else { | |
L2CC->L2CC_ECFGR1 = (source | it); | |
} | |
} | |
uint32_t l2cc_event_counter_value(uint8_t event_counter) | |
{ | |
assert((event_counter > 1) ? 0 : 1); | |
if (!event_counter) { | |
return L2CC->L2CC_EVR0; | |
} else { | |
return L2CC->L2CC_EVR1; | |
} | |
} | |
void l2cc_enable_it(uint16_t sources) | |
{ | |
L2CC->L2CC_IMR |= sources; | |
} | |
void l2cc_disable_it(uint16_t sources) | |
{ | |
L2CC->L2CC_IMR &= (!sources); | |
} | |
unsigned short l2cc_it_status_raw(uint16_t sources) | |
{ | |
return ((L2CC->L2CC_RISR) & sources) ? 1 : 0; | |
} | |
uint16_t l2cc_it_status_mask(uint16_t sources) | |
{ | |
return ((L2CC->L2CC_MISR) & sources) ? 1 : 0; | |
} | |
void l2cc_it_clear(uint16_t sources) | |
{ | |
L2CC->L2CC_ICR |= sources; | |
} | |
uint8_t l2cc_poll_spniden() | |
{ | |
return ((L2CC->L2CC_DCR & L2CC_DCR_SPNIDEN) >> 2); | |
} | |
void l2cc_cache_sync() | |
{ | |
while ((L2CC->L2CC_CSR) & L2CC_CSR_C) ; | |
L2CC->L2CC_CSR = L2CC_CSR_C; | |
while ((L2CC->L2CC_CSR) & L2CC_CSR_C) ; | |
} | |
void l2cc_invalidate_pal(uint32_t phys_addr) | |
{ | |
static uint32_t Tag; | |
static uint16_t Index; | |
Tag = (phys_addr >> (OFFSET_BIT + INDEX_BIT)); | |
Index = (phys_addr >> OFFSET_BIT) & ((1 << INDEX_BIT) - 1); | |
L2CC->L2CC_IPALR = (L2CC_IPALR_TAG(Tag) | L2CC_IPALR_IDX(Index) | L2CC_IPALR_C); | |
while ((L2CC->L2CC_IPALR) & L2CC_IPALR_C) ; | |
} | |
void l2cc_clean_pal(uint32_t phys_addr) | |
{ | |
static uint32_t Tag; | |
static uint16_t Index; | |
Tag = (phys_addr >> (OFFSET_BIT + INDEX_BIT)); | |
Index = (phys_addr >> OFFSET_BIT) & ((1 << INDEX_BIT) - 1); | |
L2CC->L2CC_CPALR = | |
(L2CC_CPALR_TAG(Tag) | L2CC_CPALR_IDX(Index) | L2CC_CPALR_C); | |
while ((L2CC->L2CC_CPALR) & L2CC_CPALR_C) ; | |
} | |
void l2cc_clean_ix(uint32_t phys_addr) | |
{ | |
static uint32_t Tag; | |
static uint16_t Index; | |
Tag = (phys_addr >> (OFFSET_BIT + INDEX_BIT)); | |
Index = (phys_addr >> OFFSET_BIT) & ((1 << INDEX_BIT) - 1); | |
L2CC->L2CC_CIPALR = | |
(L2CC_CIPALR_TAG(Tag) | L2CC_CIPALR_IDX(Index) | L2CC_CIPALR_C); | |
while ((L2CC->L2CC_CIPALR) & L2CC_CIPALR_C) ; | |
} | |
void l2cc_invalidate_way(uint8_t way) | |
{ | |
L2CC->L2CC_IWR = way; | |
while (L2CC->L2CC_IWR) ; | |
while (L2CC->L2CC_CSR) ; | |
} | |
void l2cc_clean_way(uint8_t way) | |
{ | |
L2CC->L2CC_CWR = way; | |
while (L2CC->L2CC_CWR) ; | |
while (L2CC->L2CC_CSR) ; | |
} | |
/** | |
* \brief Clean Invalidate cache by way | |
* \param way way number | |
*/ | |
static void l2cc_clean_invalidate_way(uint8_t way) | |
{ | |
L2CC->L2CC_CIWR = way; | |
while (L2CC->L2CC_CSR) ; | |
} | |
void l2cc_clean_index(uint32_t phys_addr, uint8_t way) | |
{ | |
static uint16_t Index; | |
Index = (phys_addr >> OFFSET_BIT) & ((1 << INDEX_BIT) - 1); | |
L2CC->L2CC_CIR = | |
(L2CC_CIR_IDX(Index) | L2CC_CIR_WAY(way) | L2CC_CIR_C); | |
while ((L2CC->L2CC_CIR) & L2CC_CIR_C) ; | |
} | |
void l2cc_clean_invalidate_index(uint32_t phys_addr, uint8_t way) | |
{ | |
static uint16_t Index; | |
(void) way; | |
Index = (phys_addr >> OFFSET_BIT) & ((1 << INDEX_BIT) - 1); | |
L2CC->L2CC_CIIR = | |
(L2CC_CIIR_IDX(Index) | L2CC_CIIR_WAY(Index) | L2CC_CIIR_C); | |
while ((L2CC->L2CC_CIIR) & L2CC_CIIR_C) ; | |
} | |
void l2cc_data_lockdown(uint8_t way) | |
{ | |
L2CC->L2CC_DLKR = way; | |
while (L2CC->L2CC_CSR) ; | |
} | |
void l2cc_instruction_lockdown(uint8_t way) | |
{ | |
L2CC->L2CC_ILKR = way; | |
while (L2CC->L2CC_CSR) ; | |
} | |
static void l2cc_clean(void) | |
{ | |
// Clean of L1; This is broadcast within the cluster | |
cp15_dcache_clean(); | |
if (l2cc_is_enabled()) { | |
// forces the address out past level 2 | |
l2cc_clean_way(0xFF); | |
// Ensures completion of the L2 clean | |
l2cc_cache_sync(); | |
} | |
} | |
static void l2cc_invalidate(void) | |
{ | |
if (l2cc_is_enabled()) { | |
// forces the address out past level 2 | |
l2cc_invalidate_way(0xFF); | |
// Ensures completion of the L2 inval | |
l2cc_cache_sync(); | |
} | |
// Inval of L1; This is broadcast within the cluster | |
cp15_dcache_invalidate(); | |
} | |
static void l2cc_clean_invalidate(void) | |
{ | |
/* Clean of L1; This is broadcast within the cluster */ | |
cp15_dcache_clean(); | |
if (l2cc_is_enabled()) { | |
/* forces the address out past level 2 */ | |
l2cc_clean_invalidate_way(0xFF); | |
/* Ensures completion of the L2 inval */ | |
l2cc_cache_sync(); | |
} | |
/* Inval of L1; This is broadcast within the cluster */ | |
cp15_dcache_invalidate(); | |
} | |
void l2cc_cache_maintenance(enum _maint_op maintenance) | |
{ | |
switch (maintenance) { | |
case L2CC_DCACHE_CLEAN: | |
l2cc_clean(); | |
break; | |
case L2CC_DCACHE_INVAL: | |
l2cc_invalidate(); | |
break; | |
case L2CC_DCACHE_FLUSH: | |
l2cc_clean_invalidate(); | |
break; | |
} | |
} | |
void l2cc_invalidate_region(uint32_t start, uint32_t end) | |
{ | |
assert(start < end); | |
uint32_t current = start & ~0x1fUL; | |
if (l2cc_is_enabled()) { | |
while (current <= end) { | |
l2cc_invalidate_pal(current); | |
current += 32; | |
} | |
l2cc_invalidate_pal(end); | |
} | |
cp15_invalidate_dcache_for_dma(start, end); | |
} | |
void l2cc_clean_region(uint32_t start, uint32_t end) | |
{ | |
assert(start < end); | |
uint32_t current = start & ~0x1fUL; | |
if (l2cc_is_enabled()) { | |
while (current <= end) { | |
l2cc_clean_pal(current); | |
current += 32; | |
} | |
l2cc_clean_pal(end); | |
} | |
cp15_clean_dcache_for_dma(start, end); | |
} | |
void l2cc_configure(const struct _l2cc_control* cfg) | |
{ | |
l2cc_event_config(0, L2CC_ECFGR0_ESRC_SRC_DRHIT, | |
L2CC_ECFGR0_EIGEN_INT_DIS); | |
l2cc_event_config(1, L2CC_ECFGR0_ESRC_SRC_DWHIT, | |
L2CC_ECFGR0_EIGEN_INT_DIS); | |
l2cc_enable_reset_counter(L2CC_RESET_BOTH_COUNTER); | |
l2cc_set_config(cfg); | |
/* Enable Prefetch */ | |
l2cc_inst_prefetch_enable(); | |
l2cc_data_prefetch_enable(); | |
/* Invalidate whole L2CC */ | |
l2cc_invalidate_way(0xFF); | |
/* Disable all L2CC Interrupt */ | |
l2cc_disable_it(0x1FF); | |
/* Clear all L2CC Interrupt */ | |
l2cc_it_clear(0xFF); | |
l2cc_exclusive_cache(true); | |
l2cc_enable(); | |
} |