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