[U-Boot] [RFC][PATCH] arm: Fix flush_dcache_range() on arm926

Marek Vasut marex at denx.de
Wed Jul 10 02:30:29 CEST 2013


The flush_dcache_range() on arm926 did not work as expected on i.MX28.

This can be observed during the operation of the FEC ethernet driver
where the driver did occasionally fail with timeout trying to transmit
a frame. The FEC ethernet driver uses DMA for transmitting the frame in
the following fashion:

0) Set bits in DMA descriptor
1) Write DMA descriptor into aligned DRAM address
2) Flush D-Cache over the descriptor
3) Start DMA
4) Invalidate D-Cache over the descriptor
5) Test if certain bits in DMA descriptor are unset

Very not so often it happened that the bits in the DMA descriptor were
still set even after hardware register -- which is not cached -- indicated
otherwise.

Here I will theoreticise, Albert, can you please correct me if I'm wrong?

This leads me to believe the DMA descriptor was still in the cacheline
after being flushed in step 2) and during the DMA gets evicted into DRAM
therefore corrupting the result of readback in 5) . By reading the ARM926
datasheet DDI0198E_arm926ejs_r0p5_trm.pdf page 50 table 2-17, it is not
clear whether cacheline that is Valid+Clean will be invalidated in the
D-Cache using the "mcr p15, 0, <Rx>, c7, c14, 1" instruction or whether
only Valid+Dirty lines are cleaned+invalidated. The other thing that is
unclear to me is whether a cacheline that is Valid+Clear is written back
into DRAM when it is evicted from cache.

Interestingly enough, running invalidate_dcache_range() after doing the
flush_dcache_range() over the descriptor solved the problem and I see no
occasional timeout anymore. This confirms my opinion that the descriptor
might remain in the cache and be written back during the DMA operation.

Signed-off-by: Marek Vasut <marex at denx.de>
Cc: Albert ARIBAUD <albert.u.boot at aribaud.net>
Cc: Fabio Estevam <fabio.estevam at freescale.com>
Cc: Tom Rini <trini at ti.com>
---
 arch/arm/cpu/arm926ejs/cache.c |   14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/arch/arm/cpu/arm926ejs/cache.c b/arch/arm/cpu/arm926ejs/cache.c
index 2740ad7..2a8da84 100644
--- a/arch/arm/cpu/arm926ejs/cache.c
+++ b/arch/arm/cpu/arm926ejs/cache.c
@@ -72,19 +72,29 @@ void invalidate_dcache_range(unsigned long start, unsigned long stop)
 	}
 }
 
-void flush_dcache_range(unsigned long start, unsigned long stop)
+static void clean_dcache_range(unsigned long start, unsigned long stop)
 {
 	if (!check_cache_range(start, stop))
 		return;
 
 	while (start < stop) {
-		asm volatile("mcr p15, 0, %0, c7, c14, 1\n" : : "r"(start));
+		asm volatile("mcr p15, 0, %0, c7, c10, 1\n" : : "r"(start));
 		start += CONFIG_SYS_CACHELINE_SIZE;
 	}
 
 	asm volatile("mcr p15, 0, %0, c7, c10, 4\n" : : "r"(0));
 }
 
+void flush_dcache_range(unsigned long start, unsigned long stop)
+{
+	/* See DDI0198E_arm926ejs_r0p5_trm.pdf p.50 Table 2-17 */
+
+	/* Write data to RAM from cachelines that are marked Valid+Dirty. */
+	clean_dcache_range(start, stop);
+	/* Invalidate all cachelines holding this RAM area (Dirty+Clean). */
+	invalidate_dcache_range(start, stop);
+}
+
 void flush_cache(unsigned long start, unsigned long size)
 {
 	flush_dcache_range(start, start + size);
-- 
1.7.10.4



More information about the U-Boot mailing list