[AVR32] Fix incorrect invalidation of shared cachelines
David Brownell [Sun, 28 Jan 2007 20:56:42 +0000 (12:56 -0800)]
Fix bug in dma_map_single(..., DMA_FROM_DEVICE) caused by incorrect
invalidation of shared cachelines at the beginning and/or end of
the specified buffer.  Those shared cachelines need to be flushed,
since they may hold valid data (which must not be discarded).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>

arch/avr32/mm/cache.c

index 450515b..fb13f72 100644 (file)
 
 void invalidate_dcache_region(void *start, size_t size)
 {
-       unsigned long v, begin, end, linesz;
+       unsigned long v, begin, end, linesz, mask;
+       int flush = 0;
 
        linesz = boot_cpu_data.dcache.linesz;
+       mask = linesz - 1;
+
+       /* when first and/or last cachelines are shared, flush them
+        * instead of invalidating ... never discard valid data!
+        */
+       begin = (unsigned long)start;
+       end = begin + size - 1;
+
+       if (begin & mask) {
+               flush_dcache_line(start);
+               begin += linesz;
+               flush = 1;
+       }
+       if ((end & mask) != mask) {
+               flush_dcache_line((void *)end);
+               end -= linesz;
+               flush = 1;
+       }
 
-       //printk("invalidate dcache: %p + %u\n", start, size);
-
-       /* You asked for it, you got it */
-       begin = (unsigned long)start & ~(linesz - 1);
-       end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
-
-       for (v = begin; v < end; v += linesz)
+       /* remaining cachelines only need invalidation */
+       for (v = begin; v <= end; v += linesz)
                invalidate_dcache_line((void *)v);
+       if (flush)
+               flush_write_buffer();
 }
 
 void clean_dcache_region(void *start, size_t size)