@@ -30,6 +30,7 @@
#ifndef __ASSEMBLY__
void cache_init(void);
+void flush_n_invalidate_dcache_all(void);
#endif /* __ASSEMBLY__ */
@@ -256,8 +256,7 @@ void cache_init(void)
/* IOC Aperture size is equal to DDR size */
long ap_size = CONFIG_SYS_SDRAM_SIZE;
- flush_dcache_all();
- invalidate_dcache_all();
+ flush_n_invalidate_dcache_all();
if (!is_power_of_2(ap_size) || ap_size < 4096)
panic("IOC Aperture size must be power of 2 and bigger 4Kib");
@@ -483,13 +482,19 @@ void flush_cache(unsigned long start, unsigned long size)
flush_dcache_range(start, start + size);
}
-void invalidate_dcache_all(void)
+/*
+ * As invalidate_dcache_all() is not used in generic U-Boot code and as we
+ * don't need it in arch/arc code alone (invalidate without flush) we implement
+ * flush_n_invalidate_dcache_all (flush and invalidate in 1 operation) because
+ * it's much safer.
+ */
+void flush_n_invalidate_dcache_all(void)
{
- __dc_entire_op(OP_INV);
+ __dc_entire_op(OP_FLUSH_N_INV);
#ifdef CONFIG_ISA_ARCV2
if (slc_exists)
- __slc_entire_op(OP_INV);
+ __slc_entire_op(OP_FLUSH_N_INV);
#endif
}
We don't implement separate flush_dcache_all intentionally as entire data cache invalidation is dangerous operation even if we flush data cache right before invalidation. There is the real example: We may hang in the next code if we store any context (like BLINK register) on stack in invalidate_dcache_all() function. BLINK register is the register where return address is automatically saved when we do function call with instructions like 'bl'. void flush_dcache_all() { __dc_entire_op(OP_FLUSH); // Other code // } void invalidate_dcache_all() { __dc_entire_op(OP_INV); // Other code // } void foo(void) { flush_dcache_all(); invalidate_dcache_all(); } Now let's see what really happens during that code execution: foo() |->> call flush_dcache_all [return address is saved to BLINK register] [push BLINK] (save to stack) ![point 1] |->> call __dc_entire_op(OP_FLUSH) [return address is saved to BLINK register] [flush L1 D$] return [jump to BLINK] <<------ [other flush_dcache_all code] [pop BLINK] (get from stack) return [jump to BLINK] <<------ |->> call invalidate_dcache_all [return address is saved to BLINK register] [push BLINK] (save to stack) ![point 2] |->> call __dc_entire_op(OP_FLUSH) [return address is saved to BLINK register] [invalidate L1 D$] ![point 3] // Oops!!! // We lose return address from invalidate_dcache_all function: // we save it to stack and invalidate L1 D$ after that! return [jump to BLINK] <<------ [other invalidate_dcache_all code] [pop BLINK] (get from stack) // we don't have this data in L1 dcache as we invalidated it in [point 3] // so we get it from next memory level (for example DDR memory) // but in the memory we have value which we save in [point 1], which // is return address from flush_dcache_all function (instead of // address from current invalidate_dcache_all function which we // saved in [point 2] !) return [jump to BLINK] <<------ // As BLINK points to invalidate_dcache_all, we call it again and // loop forever. Fortunately we may do flush and invalidation of D$ with a single one instruction which automatically mitigates a situation described above. And because invalidate_dcache_all isn't used in common u-boot code we implement "flush and invalidate dcache all" instead. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> --- arch/arc/include/asm/cache.h | 1 + arch/arc/lib/cache.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-)