u-boot/arch/arc/cpu/arc700/cache.c
Igor Guryanov f8cf3d1ebd arc: check caches existence before use
Some cache operations ({i|d}cache_{enable|disable|status} or
flush_dcache_all) are built and used even if CONFIG_SYS_{I|D}CACHE_OFF
is set.

This is required for force disable of caches on early boot.
What if something was executed before U-boot and enabled caches
(low-level bootloaders, previously run kernel etc.)?

But if CPU doesn't really have caches any attempt to access
cache-related AUX registers triggers instruction error exception.

So for convenience we'll try to avoid exceptions by checking if CPU
actually has caches (we check separately data and instruction cache
existence) at all.

Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
Signed-off-by: Igor Guryanov <guryanov@synopsys.com>
2015-01-15 22:38:42 +03:00

167 lines
4.1 KiB
C

/*
* Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm/arcregs.h>
/* Bit values in IC_CTRL */
#define IC_CTRL_CACHE_DISABLE (1 << 0)
/* Bit values in DC_CTRL */
#define DC_CTRL_CACHE_DISABLE (1 << 0)
#define DC_CTRL_INV_MODE_FLUSH (1 << 6)
#define DC_CTRL_FLUSH_STATUS (1 << 8)
#define CACHE_VER_NUM_MASK 0xF
int icache_status(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_IC_BUILD) & CACHE_VER_NUM_MASK))
return 0;
return (read_aux_reg(ARC_AUX_IC_CTRL) & IC_CTRL_CACHE_DISABLE) !=
IC_CTRL_CACHE_DISABLE;
}
void icache_enable(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_IC_BUILD) & CACHE_VER_NUM_MASK))
return;
write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) &
~IC_CTRL_CACHE_DISABLE);
}
void icache_disable(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_IC_BUILD) & CACHE_VER_NUM_MASK))
return;
write_aux_reg(ARC_AUX_IC_CTRL, read_aux_reg(ARC_AUX_IC_CTRL) |
IC_CTRL_CACHE_DISABLE);
}
void invalidate_icache_all(void)
{
#ifndef CONFIG_SYS_ICACHE_OFF
/* Any write to IC_IVIC register triggers invalidation of entire I$ */
write_aux_reg(ARC_AUX_IC_IVIC, 1);
#endif /* CONFIG_SYS_ICACHE_OFF */
}
int dcache_status(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK))
return 0;
return (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_CACHE_DISABLE) !=
DC_CTRL_CACHE_DISABLE;
}
void dcache_enable(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK))
return;
write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) &
~(DC_CTRL_INV_MODE_FLUSH | DC_CTRL_CACHE_DISABLE));
}
void dcache_disable(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK))
return;
write_aux_reg(ARC_AUX_DC_CTRL, read_aux_reg(ARC_AUX_DC_CTRL) |
DC_CTRL_CACHE_DISABLE);
}
void flush_dcache_all(void)
{
/* If no cache in CPU exit immediately */
if (!(read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK))
return;
/* Do flush of entire cache */
write_aux_reg(ARC_AUX_DC_FLSH, 1);
/* Wait flush end */
while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
;
}
#ifndef CONFIG_SYS_DCACHE_OFF
static void dcache_flush_line(unsigned addr)
{
#if (CONFIG_ARC_MMU_VER > 2)
write_aux_reg(ARC_AUX_DC_PTAG, addr);
#endif
write_aux_reg(ARC_AUX_DC_FLDL, addr);
/* Wait flush end */
while (read_aux_reg(ARC_AUX_DC_CTRL) & DC_CTRL_FLUSH_STATUS)
;
#ifndef CONFIG_SYS_ICACHE_OFF
/*
* Invalidate I$ for addresses range just flushed from D$.
* If we try to execute data flushed above it will be valid/correct
*/
#if (CONFIG_ARC_MMU_VER > 2)
write_aux_reg(ARC_AUX_IC_PTAG, addr);
#endif
write_aux_reg(ARC_AUX_IC_IVIL, addr);
#endif /* CONFIG_SYS_ICACHE_OFF */
}
#endif /* CONFIG_SYS_DCACHE_OFF */
void flush_dcache_range(unsigned long start, unsigned long end)
{
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int addr;
start = start & (~(CONFIG_SYS_CACHELINE_SIZE - 1));
end = end & (~(CONFIG_SYS_CACHELINE_SIZE - 1));
for (addr = start; addr <= end; addr += CONFIG_SYS_CACHELINE_SIZE)
dcache_flush_line(addr);
#endif /* CONFIG_SYS_DCACHE_OFF */
}
void invalidate_dcache_range(unsigned long start, unsigned long end)
{
#ifndef CONFIG_SYS_DCACHE_OFF
unsigned int addr;
start = start & (~(CONFIG_SYS_CACHELINE_SIZE - 1));
end = end & (~(CONFIG_SYS_CACHELINE_SIZE - 1));
for (addr = start; addr <= end; addr += CONFIG_SYS_CACHELINE_SIZE) {
#if (CONFIG_ARC_MMU_VER > 2)
write_aux_reg(ARC_AUX_DC_PTAG, addr);
#endif
write_aux_reg(ARC_AUX_DC_IVDL, addr);
}
#endif /* CONFIG_SYS_DCACHE_OFF */
}
void invalidate_dcache_all(void)
{
#ifndef CONFIG_SYS_DCACHE_OFF
/* Write 1 to DC_IVDC register triggers invalidation of entire D$ */
write_aux_reg(ARC_AUX_DC_IVDC, 1);
#endif /* CONFIG_SYS_DCACHE_OFF */
}
void flush_cache(unsigned long start, unsigned long size)
{
flush_dcache_range(start, start + size);
}