MIPS: L2 cache support
This patch adds support for initialising & maintaining L2 caches on MIPS
systems. The L2 cache configuration may be advertised through either
coprocessor 0 or the MIPS Coherence Manager depending upon the system,
and support for both is included.
If the L2 can be bypassed then we bypass it early in boot & initialise
the L1 caches first, such that we can start making use of the L1
instruction cache as early as possible. Otherwise we initialise the L2
first such that the L1s have no opportunity to generate access to the
uninitialised L2.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
diff --git a/arch/mips/lib/cache.c b/arch/mips/lib/cache.c
index d8baf08..bd14ba6 100644
--- a/arch/mips/lib/cache.c
+++ b/arch/mips/lib/cache.c
@@ -7,10 +7,44 @@
#include <common.h>
#include <asm/cacheops.h>
+#include <asm/cm.h>
#include <asm/mipsregs.h>
DECLARE_GLOBAL_DATA_PTR;
+static void probe_l2(void)
+{
+#ifdef CONFIG_MIPS_L2_CACHE
+ unsigned long conf2, sl;
+ bool l2c = false;
+
+ if (!(read_c0_config1() & MIPS_CONF_M))
+ return;
+
+ conf2 = read_c0_config2();
+
+ if (__mips_isa_rev >= 6) {
+ l2c = conf2 & MIPS_CONF_M;
+ if (l2c)
+ l2c = read_c0_config3() & MIPS_CONF_M;
+ if (l2c)
+ l2c = read_c0_config4() & MIPS_CONF_M;
+ if (l2c)
+ l2c = read_c0_config5() & MIPS_CONF5_L2C;
+ }
+
+ if (l2c && config_enabled(CONFIG_MIPS_CM)) {
+ gd->arch.l2_line_size = mips_cm_l2_line_size();
+ } else if (l2c) {
+ /* We don't know how to retrieve L2 config on this system */
+ BUG();
+ } else {
+ sl = (conf2 & MIPS_CONF2_SL) >> MIPS_CONF2_SL_SHF;
+ gd->arch.l2_line_size = sl ? (2 << sl) : 0;
+ }
+#endif
+}
+
void mips_cache_probe(void)
{
#ifdef CONFIG_SYS_CACHE_SIZE_AUTO
@@ -24,6 +58,7 @@
gd->arch.l1i_line_size = il ? (2 << il) : 0;
gd->arch.l1d_line_size = dl ? (2 << dl) : 0;
#endif
+ probe_l2();
}
static inline unsigned long icache_line_size(void)
@@ -44,6 +79,15 @@
#endif
}
+static inline unsigned long scache_line_size(void)
+{
+#ifdef CONFIG_MIPS_L2_CACHE
+ return gd->arch.l2_line_size;
+#else
+ return 0;
+#endif
+}
+
#define cache_loop(start, end, lsize, ops...) do { \
const void *addr = (const void *)(start & ~(lsize - 1)); \
const void *aend = (const void *)((end - 1) & ~(lsize - 1)); \
@@ -60,12 +104,13 @@
{
unsigned long ilsize = icache_line_size();
unsigned long dlsize = dcache_line_size();
+ unsigned long slsize = scache_line_size();
/* aend will be miscalculated when size is zero, so we return here */
if (size == 0)
return;
- if (ilsize == dlsize) {
+ if ((ilsize == dlsize) && !slsize) {
/* flush I-cache & D-cache simultaneously */
cache_loop(start_addr, start_addr + size, ilsize,
HIT_WRITEBACK_INV_D, HIT_INVALIDATE_I);
@@ -75,6 +120,11 @@
/* flush D-cache */
cache_loop(start_addr, start_addr + size, dlsize, HIT_WRITEBACK_INV_D);
+ /* flush L2 cache */
+ if (slsize)
+ cache_loop(start_addr, start_addr + size, slsize,
+ HIT_WRITEBACK_INV_SD);
+
/* flush I-cache */
cache_loop(start_addr, start_addr + size, ilsize, HIT_INVALIDATE_I);
}
@@ -82,21 +132,31 @@
void flush_dcache_range(ulong start_addr, ulong stop)
{
unsigned long lsize = dcache_line_size();
+ unsigned long slsize = scache_line_size();
/* aend will be miscalculated when size is zero, so we return here */
if (start_addr == stop)
return;
cache_loop(start_addr, stop, lsize, HIT_WRITEBACK_INV_D);
+
+ /* flush L2 cache */
+ if (slsize)
+ cache_loop(start_addr, stop, slsize, HIT_WRITEBACK_INV_SD);
}
void invalidate_dcache_range(ulong start_addr, ulong stop)
{
unsigned long lsize = dcache_line_size();
+ unsigned long slsize = scache_line_size();
/* aend will be miscalculated when size is zero, so we return here */
if (start_addr == stop)
return;
+ /* invalidate L2 cache */
+ if (slsize)
+ cache_loop(start_addr, stop, slsize, HIT_INVALIDATE_SD);
+
cache_loop(start_addr, stop, lsize, HIT_INVALIDATE_D);
}