mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-14 17:07:38 +00:00
fbb0de088b
This patch makes sure that the flush/invalidate_dcache_range() functions can handle corner-case calls like this -- invalidate_dcache_range(0, 0, 0); This call is valid and is happily produced by USB EHCI code for example. The expected behavior of the cache function(s) in this case is that they will do no operation, since the size is zero. The current implementation though has a problem where such invocation will result in a hard CPU hang. This is because under such conditions, where the start_addr = 0 and stop = 0, the addr = 0 and aend = 0xffffffe0 . The loop will then try to iterate over the entire address space, which in itself is wrong. But iterating over the entire address space might also hit some odd address which will cause bus hang. The later happens on the Atheros MIPS. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> Cc: Hans de Goede <hdegoede@redhat.com>
126 lines
2.7 KiB
C
126 lines
2.7 KiB
C
/*
|
|
* (C) Copyright 2003
|
|
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/cacheops.h>
|
|
#include <asm/mipsregs.h>
|
|
|
|
#ifdef CONFIG_SYS_CACHELINE_SIZE
|
|
|
|
static inline unsigned long icache_line_size(void)
|
|
{
|
|
return CONFIG_SYS_CACHELINE_SIZE;
|
|
}
|
|
|
|
static inline unsigned long dcache_line_size(void)
|
|
{
|
|
return CONFIG_SYS_CACHELINE_SIZE;
|
|
}
|
|
|
|
#else /* !CONFIG_SYS_CACHELINE_SIZE */
|
|
|
|
static inline unsigned long icache_line_size(void)
|
|
{
|
|
unsigned long conf1, il;
|
|
conf1 = read_c0_config1();
|
|
il = (conf1 & MIPS_CONF1_IL) >> MIPS_CONF1_IL_SHF;
|
|
if (!il)
|
|
return 0;
|
|
return 2 << il;
|
|
}
|
|
|
|
static inline unsigned long dcache_line_size(void)
|
|
{
|
|
unsigned long conf1, dl;
|
|
conf1 = read_c0_config1();
|
|
dl = (conf1 & MIPS_CONF1_DL) >> MIPS_CONF1_DL_SHF;
|
|
if (!dl)
|
|
return 0;
|
|
return 2 << dl;
|
|
}
|
|
|
|
#endif /* !CONFIG_SYS_CACHELINE_SIZE */
|
|
|
|
void flush_cache(ulong start_addr, ulong size)
|
|
{
|
|
unsigned long ilsize = icache_line_size();
|
|
unsigned long dlsize = dcache_line_size();
|
|
const void *addr, *aend;
|
|
|
|
/* aend will be miscalculated when size is zero, so we return here */
|
|
if (size == 0)
|
|
return;
|
|
|
|
addr = (const void *)(start_addr & ~(dlsize - 1));
|
|
aend = (const void *)((start_addr + size - 1) & ~(dlsize - 1));
|
|
|
|
if (ilsize == dlsize) {
|
|
/* flush I-cache & D-cache simultaneously */
|
|
while (1) {
|
|
mips_cache(HIT_WRITEBACK_INV_D, addr);
|
|
mips_cache(HIT_INVALIDATE_I, addr);
|
|
if (addr == aend)
|
|
break;
|
|
addr += dlsize;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* flush D-cache */
|
|
while (1) {
|
|
mips_cache(HIT_WRITEBACK_INV_D, addr);
|
|
if (addr == aend)
|
|
break;
|
|
addr += dlsize;
|
|
}
|
|
|
|
/* flush I-cache */
|
|
addr = (const void *)(start_addr & ~(ilsize - 1));
|
|
aend = (const void *)((start_addr + size - 1) & ~(ilsize - 1));
|
|
while (1) {
|
|
mips_cache(HIT_INVALIDATE_I, addr);
|
|
if (addr == aend)
|
|
break;
|
|
addr += ilsize;
|
|
}
|
|
}
|
|
|
|
void flush_dcache_range(ulong start_addr, ulong stop)
|
|
{
|
|
unsigned long lsize = dcache_line_size();
|
|
const void *addr = (const void *)(start_addr & ~(lsize - 1));
|
|
const void *aend = (const void *)((stop - 1) & ~(lsize - 1));
|
|
|
|
/* aend will be miscalculated when size is zero, so we return here */
|
|
if (start_addr == stop)
|
|
return;
|
|
|
|
while (1) {
|
|
mips_cache(HIT_WRITEBACK_INV_D, addr);
|
|
if (addr == aend)
|
|
break;
|
|
addr += lsize;
|
|
}
|
|
}
|
|
|
|
void invalidate_dcache_range(ulong start_addr, ulong stop)
|
|
{
|
|
unsigned long lsize = dcache_line_size();
|
|
const void *addr = (const void *)(start_addr & ~(lsize - 1));
|
|
const void *aend = (const void *)((stop - 1) & ~(lsize - 1));
|
|
|
|
/* aend will be miscalculated when size is zero, so we return here */
|
|
if (start_addr == stop)
|
|
return;
|
|
|
|
while (1) {
|
|
mips_cache(HIT_INVALIDATE_D, addr);
|
|
if (addr == aend)
|
|
break;
|
|
addr += lsize;
|
|
}
|
|
}
|