diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2007-01-28 12:56:42 -0800 |
---|---|---|
committer | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-02-09 15:01:57 +0100 |
commit | 212868d387d0033b7e0029326a900126fe5e3d52 (patch) | |
tree | dc4209d294def354963f22051caa7832f8de98c1 /arch | |
parent | 58febc0b1374de7506277d3aa9e9cddaea62ba65 (diff) |
[AVR32] Fix incorrect invalidation of shared cachelines
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>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/avr32/mm/cache.c | 32 |
1 files changed, 24 insertions, 8 deletions
diff --git a/arch/avr32/mm/cache.c b/arch/avr32/mm/cache.c index 450515b245a..fb13f72e9a0 100644 --- a/arch/avr32/mm/cache.c +++ b/arch/avr32/mm/cache.c @@ -22,18 +22,34 @@ 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) |