linux/arch/v850/kernel/v850e2_cache.c

128 lines
3.7 KiB
C
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* arch/v850/kernel/v850e2_cache.c -- Cache control for V850E2 cache
* memories
*
* Copyright (C) 2003 NEC Electronics Corporation
* Copyright (C) 2003 Miles Bader <miles@gnu.org>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of this
* archive for more details.
*
* Written by Miles Bader <miles@gnu.org>
*/
#include <linux/mm.h>
#include <asm/v850e2_cache.h>
/* Cache operations we can do. The encoding corresponds directly to the
value we need to write into the COPR register. */
enum cache_op {
OP_SYNC_IF_DIRTY = V850E2_CACHE_COPR_CFC(0), /* 000 */
OP_SYNC_IF_VALID = V850E2_CACHE_COPR_CFC(1), /* 001 */
OP_SYNC_IF_VALID_AND_CLEAR = V850E2_CACHE_COPR_CFC(3), /* 011 */
OP_WAY_CLEAR = V850E2_CACHE_COPR_CFC(4), /* 100 */
OP_FILL = V850E2_CACHE_COPR_CFC(5), /* 101 */
OP_CLEAR = V850E2_CACHE_COPR_CFC(6), /* 110 */
OP_CREATE_DIRTY = V850E2_CACHE_COPR_CFC(7) /* 111 */
};
/* Which cache to use. This encoding also corresponds directly to the
value we need to write into the COPR register. */
enum cache {
ICACHE = 0,
DCACHE = V850E2_CACHE_COPR_LBSL
};
/* Returns ADDR rounded down to the beginning of its cache-line. */
#define CACHE_LINE_ADDR(addr) \
((addr) & ~(V850E2_CACHE_LINE_SIZE - 1))
/* Returns END_ADDR rounded up to the `limit' of its cache-line. */
#define CACHE_LINE_END_ADDR(end_addr) \
CACHE_LINE_ADDR(end_addr + (V850E2_CACHE_LINE_SIZE - 1))
/* Low-level cache ops. */
/* Apply cache-op OP to all entries in CACHE. */
static inline void cache_op_all (enum cache_op op, enum cache cache)
{
int cmd = op | cache | V850E2_CACHE_COPR_WSLE | V850E2_CACHE_COPR_STRT;
if (op != OP_WAY_CLEAR) {
/* The WAY_CLEAR operation does the whole way, but other
ops take begin-index and count params; we just indicate
the entire cache. */
V850E2_CACHE_CADL = 0;
V850E2_CACHE_CADH = 0;
V850E2_CACHE_CCNT = V850E2_CACHE_WAY_SIZE - 1;
}
V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(0); /* way 0 */
V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(1); /* way 1 */
V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(2); /* way 2 */
V850E2_CACHE_COPR = cmd | V850E2_CACHE_COPR_WSL(3); /* way 3 */
}
/* Apply cache-op OP to all entries in CACHE covering addresses ADDR
through ADDR+LEN. */
static inline void cache_op_range (enum cache_op op, u32 addr, u32 len,
enum cache cache)
{
u32 start = CACHE_LINE_ADDR (addr);
u32 end = CACHE_LINE_END_ADDR (addr + len);
u32 num_lines = (end - start) >> V850E2_CACHE_LINE_SIZE_BITS;
V850E2_CACHE_CADL = start & 0xFFFF;
V850E2_CACHE_CADH = start >> 16;
V850E2_CACHE_CCNT = num_lines - 1;
V850E2_CACHE_COPR = op | cache | V850E2_CACHE_COPR_STRT;
}
/* High-level ops. */
static void cache_exec_after_store_all (void)
{
cache_op_all (OP_SYNC_IF_DIRTY, DCACHE);
cache_op_all (OP_WAY_CLEAR, ICACHE);
}
static void cache_exec_after_store_range (u32 start, u32 len)
{
cache_op_range (OP_SYNC_IF_DIRTY, start, len, DCACHE);
cache_op_range (OP_CLEAR, start, len, ICACHE);
}
/* Exported functions. */
void flush_icache (void)
{
cache_exec_after_store_all ();
}
void flush_icache_range (unsigned long start, unsigned long end)
{
cache_exec_after_store_range (start, end - start);
}
void flush_icache_page (struct vm_area_struct *vma, struct page *page)
{
cache_exec_after_store_range (page_to_virt (page), PAGE_SIZE);
}
void flush_icache_user_range (struct vm_area_struct *vma, struct page *page,
unsigned long addr, int len)
{
cache_exec_after_store_range (addr, len);
}
void flush_cache_sigtramp (unsigned long addr)
{
/* For the exact size, see signal.c, but 16 bytes should be enough. */
cache_exec_after_store_range (addr, 16);
}