mips32: detect L1 cache sizes if they're not defined
For boards such as the MIPS Malta with an FPGA core card it is desirable
to be able to detect the L1 cache sizes at runtime, since they are not
dependant upon the board but on the FPGA bitstream in use. This patch
performs that detection when the CONFIG_SYS_[DI]CACHE_SIZE macros are
not defined by the board configuration. In cases where the sizes are
detected this patch also removes the restriction that the I-cache &
D-cache line sizes must be the same, as this is not necessarily true.
If the cache sizes are defined by a configuration then they will be
hardcoded as before, so this patch will not add overhead to such
boards.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
diff --git a/arch/mips/cpu/mips32/cpu.c b/arch/mips/cpu/mips32/cpu.c
index 28d5c45..278865b 100644
--- a/arch/mips/cpu/mips32/cpu.c
+++ b/arch/mips/cpu/mips32/cpu.c
@@ -34,28 +34,89 @@
return 0;
}
+#ifdef CONFIG_SYS_CACHELINE_SIZE
+
+static inline unsigned long icache_line_size(void)
+{
+ return CONFIG_SYS_CACHELINE_SIZE;
+}
+
+static inline unsigned long dcache_line_size(void)
+{
+ return CONFIG_SYS_CACHELINE_SIZE;
+}
+
+#else /* !CONFIG_SYS_CACHELINE_SIZE */
+
+static inline unsigned long icache_line_size(void)
+{
+ unsigned long conf1, il;
+ conf1 = read_c0_config1();
+ il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHIFT;
+ if (!il)
+ return 0;
+ return 2 << il;
+}
+
+static inline unsigned long dcache_line_size(void)
+{
+ unsigned long conf1, dl;
+ conf1 = read_c0_config1();
+ dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHIFT;
+ if (!dl)
+ return 0;
+ return 2 << dl;
+}
+
+#endif /* !CONFIG_SYS_CACHELINE_SIZE */
+
void flush_cache(ulong start_addr, ulong size)
{
- unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
- unsigned long addr = start_addr & ~(lsize - 1);
- unsigned long aend = (start_addr + size - 1) & ~(lsize - 1);
+ unsigned long ilsize = icache_line_size();
+ unsigned long dlsize = dcache_line_size();
+ unsigned long addr, aend;
/* aend will be miscalculated when size is zero, so we return here */
if (size == 0)
return;
+ addr = start_addr & ~(dlsize - 1);
+ aend = (start_addr + size - 1) & ~(dlsize - 1);
+
+ if (ilsize == dlsize) {
+ /* flush I-cache & D-cache simultaneously */
+ while (1) {
+ cache_op(HIT_WRITEBACK_INV_D, addr);
+ cache_op(HIT_INVALIDATE_I, addr);
+ if (addr == aend)
+ break;
+ addr += dlsize;
+ }
+ return;
+ }
+
+ /* flush D-cache */
while (1) {
cache_op(HIT_WRITEBACK_INV_D, addr);
+ if (addr == aend)
+ break;
+ addr += dlsize;
+ }
+
+ /* flush I-cache */
+ addr = start_addr & ~(ilsize - 1);
+ aend = (start_addr + size - 1) & ~(ilsize - 1);
+ while (1) {
cache_op(HIT_INVALIDATE_I, addr);
if (addr == aend)
break;
- addr += lsize;
+ addr += ilsize;
}
}
void flush_dcache_range(ulong start_addr, ulong stop)
{
- unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
+ unsigned long lsize = dcache_line_size();
unsigned long addr = start_addr & ~(lsize - 1);
unsigned long aend = (stop - 1) & ~(lsize - 1);
@@ -69,7 +130,7 @@
void invalidate_dcache_range(ulong start_addr, ulong stop)
{
- unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE;
+ unsigned long lsize = dcache_line_size();
unsigned long addr = start_addr & ~(lsize - 1);
unsigned long aend = (stop - 1) & ~(lsize - 1);