| /* |
| * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <config.h> |
| #include <asm/arcregs.h> |
| #include <asm/cache.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 |
| #define SLC_CTRL_SB (1 << 2) |
| |
| 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) |
| { |
| /* If no cache in CPU exit immediately */ |
| if (!(read_aux_reg(ARC_BCR_IC_BUILD) & CACHE_VER_NUM_MASK)) |
| return; |
| |
| /* Any write to IC_IVIC register triggers invalidation of entire I$ */ |
| write_aux_reg(ARC_AUX_IC_IVIC, 1); |
| } |
| |
| 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 == 3) |
| 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 == 3) |
| 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 == 3) |
| 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) |
| { |
| /* If no cache in CPU exit immediately */ |
| if (!(read_aux_reg(ARC_BCR_DC_BUILD) & CACHE_VER_NUM_MASK)) |
| return; |
| |
| /* Write 1 to DC_IVDC register triggers invalidation of entire D$ */ |
| write_aux_reg(ARC_AUX_DC_IVDC, 1); |
| } |
| |
| void flush_cache(unsigned long start, unsigned long size) |
| { |
| flush_dcache_range(start, start + size); |
| } |
| |
| #ifdef CONFIG_ISA_ARCV2 |
| void slc_enable(void) |
| { |
| /* If SLC ver = 0, no SLC present in CPU */ |
| if (!(read_aux_reg(ARC_BCR_SLC) & 0xff)) |
| return; |
| |
| write_aux_reg(ARC_AUX_SLC_CONTROL, |
| read_aux_reg(ARC_AUX_SLC_CONTROL) & ~1); |
| } |
| |
| void slc_disable(void) |
| { |
| /* If SLC ver = 0, no SLC present in CPU */ |
| if (!(read_aux_reg(ARC_BCR_SLC) & 0xff)) |
| return; |
| |
| write_aux_reg(ARC_AUX_SLC_CONTROL, |
| read_aux_reg(ARC_AUX_SLC_CONTROL) | 1); |
| } |
| |
| void slc_flush(void) |
| { |
| /* If SLC ver = 0, no SLC present in CPU */ |
| if (!(read_aux_reg(ARC_BCR_SLC) & 0xff)) |
| return; |
| |
| write_aux_reg(ARC_AUX_SLC_FLUSH, 1); |
| |
| /* Wait flush end */ |
| while (read_aux_reg(ARC_AUX_SLC_CONTROL) & SLC_CTRL_SB) |
| ; |
| } |
| |
| void slc_invalidate(void) |
| { |
| /* If SLC ver = 0, no SLC present in CPU */ |
| if (!(read_aux_reg(ARC_BCR_SLC) & 0xff)) |
| return; |
| |
| write_aux_reg(ARC_AUX_SLC_INVALIDATE, 1); |
| } |
| |
| #endif /* CONFIG_ISA_ARCV2 */ |