
After DMA operation, we need to maintain D-Cache coherency. So that the DCache must be invalidated (hence CPU will fetch data written by DMA controller from RAM).
Tested on AT91SAM9261EK with Peripheral DMA controller.
Signed-off-by: Hong Xu hong.xu@atmel.com Tested-by: Elen Song elen.song@atmel.com CC: Albert Aribaud albert.u.boot@aribaud.net CC: Aneesh V aneesh@ti.com CC: Marek Vasut marek.vasut@gmail.com CC: Reinhard Meyer u-boot@emk-elektronik.de CC: Heiko Schocher hs@denx.de --- V2: Per Albert's suggestion, add invalidate_dcache_range
V3: invalidate_dcache_range emits warning when detecting unaligned buffer
invalidate_dcache_range won't clean any adjacent cache line when detecting unaligned buffer and only round up/down the buffer address
v4: invalidate_dcache_range will emit clearer warning message
Per Albert's suggestion, if not alighed to cache line size, round up start address, round down stop addres
Per Marek Vasut's suggestion, use __func__ stated in C99
arch/arm/lib/cache.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 58 insertions(+), 0 deletions(-)
diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c index 92b61a2..4c8e160 100644 --- a/arch/arm/lib/cache.c +++ b/arch/arm/lib/cache.c @@ -53,3 +53,61 @@ void __flush_dcache_all(void) } void flush_dcache_all(void) __attribute__((weak, alias("__flush_dcache_all"))); + +/* + * The buffer range to be invalidated is [start, stop) + */ +void __invalidate_dcache_range(unsigned long start, unsigned long stop) +{ + int cache_line_len; + unsigned long mva; + +#ifdef CONFIG_ARM926EJS +#ifdef CONFIG_SYS_CACHELINE_SIZE + cache_line_len = CONFIG_SYS_CACHELINE_SIZE; +#else + /* + * ARM926EJ-S Technical Reference Manual, Chap 2.3.1 Table 2-9 + * only b'10, aka. 32 bytes cache line len is valid + */ + cache_line_len = 32; +#endif + mva = start; + if ((mva & (cache_line_len - 1)) != 0) { + printf("WARNING: %s - start address 0x%08x not aligned to" + "cache line size(%d bytes)\n", __func__, start, + cache_line_len); + /* Round up starting address */ + mva = (mva | (cache_line_len - 1)) + 1; + } + if ((stop & (cache_line_len - 1)) != 0) { + printf("WARNING: %s - stop address 0x%08x not aligned to" + "cache line size(%d bytes)\n", __func__, stop, + cache_line_len); + /* Round down ending address */ + stop &= ~(cache_line_len - 1); + } + + while (mva < stop) { + asm("mcr p15, 0, %0, c7, c6, 1" : : "r"(mva)); + mva += cache_line_len; + } + + /* Drain the WB */ + asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); +#endif + + return; +} +void invalidate_dcache_range(unsigned long start, unsigned long stop) + __attribute__((weak, alias("__invalidate_dcache_range"))); + +void __invalidate_dcache_all(void) +{ +#ifdef CONFIG_ARM926EJS + asm("mcr p15, 0, %0, c7, c6, 0" : : "r" (0)); +#endif + return; +} +void invalidate_dcache_all(void) + __attribute__((weak, alias("__invalidate_dcache_all")));