|
|
|
|
/*
|
|
|
|
|
* 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);
|
|
|
|
|
}
|