diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
commit | 546db14ee74118296f425f3b91634fb767d67290 (patch) | |
tree | 22b613a3da8d4bf663eec5e155af01b87fdf9094 /arch/sh/mm/cache.c | |
parent | 1e25e41c4f5474e14452094492dbc169b800e4c8 (diff) |
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles
and IP27 actually works.
Diffstat (limited to 'arch/sh/mm/cache.c')
-rw-r--r-- | arch/sh/mm/cache.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/arch/sh/mm/cache.c b/arch/sh/mm/cache.c new file mode 100644 index 000000000..129aba7cc --- /dev/null +++ b/arch/sh/mm/cache.c @@ -0,0 +1,366 @@ +/* $Id$ + * + * linux/arch/sh/mm/cache.c + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/threads.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cache.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +#if defined(__sh3__) +#define CCR 0xffffffec /* Address of Cache Control Register */ +#define CCR_CACHE_VAL 0x00000005 /* 8k-byte cache, P1-wb, enable */ +#define CCR_CACHE_INIT 0x0000000d /* 8k-byte cache, CF, P1-wb, enable */ +#define CCR_CACHE_ENABLE 1 + +#define CACHE_IC_ADDRESS_ARRAY 0xf0000000 /* SH-3 has unified cache system */ +#define CACHE_OC_ADDRESS_ARRAY 0xf0000000 +#define CACHE_VALID 1 +#define CACHE_UPDATED 2 + +/* 7709A/7729 has 16K cache (256-entry), while 7702 has only 2K(direct) + 7702 is not supported (yet) */ +struct _cache_system_info { + int way_shift; + int entry_mask; + int num_entries; +}; + +static struct _cache_system_info cache_system_info; + +#define CACHE_OC_WAY_SHIFT (cache_system_info.way_shift) +#define CACHE_IC_WAY_SHIFT (cache_system_info.way_shift) +#define CACHE_OC_ENTRY_SHIFT 4 +#define CACHE_OC_ENTRY_MASK (cache_system_info.entry_mask) +#define CACHE_IC_ENTRY_MASK (cache_system_info.entry_mask) +#define CACHE_OC_NUM_ENTRIES (cache_system_info.num_entries) +#define CACHE_OC_NUM_WAYS 4 +#define CACHE_IC_NUM_WAYS 4 +#elif defined(__SH4__) +#define CCR 0xff00001c /* Address of Cache Control Register */ +#define CCR_CACHE_VAL 0x00000105 /* 8k+16k-byte cache,P1-wb,enable */ +#define CCR_CACHE_INIT 0x0000090d /* 8k+16k-byte cache,CF,P1-wb,enable */ +#define CCR_CACHE_ENABLE 0x00000101 + +#define CACHE_IC_ADDRESS_ARRAY 0xf0000000 +#define CACHE_OC_ADDRESS_ARRAY 0xf4000000 +#define CACHE_VALID 1 +#define CACHE_UPDATED 2 + +#define CACHE_OC_WAY_SHIFT 13 +#define CACHE_IC_WAY_SHIFT 13 +#define CACHE_OC_ENTRY_SHIFT 5 +#define CACHE_OC_ENTRY_MASK 0x3fe0 +#define CACHE_IC_ENTRY_MASK 0x1fe0 +#define CACHE_OC_NUM_ENTRIES 512 +#define CACHE_OC_NUM_WAYS 1 +#define CACHE_IC_NUM_WAYS 1 +#endif + +#define jump_to_p2(__dummy) \ + asm volatile("mova 1f,%0\n\t" \ + "add %1,%0\n\t" \ + "jmp @r0 ! Jump to P2 area\n\t" \ + " nop\n\t" \ + ".balign 4\n" \ + "1:" \ + : "=&z" (__dummy) \ + : "r" (0x20000000)) + +#define back_to_p1(__dummy) \ + asm volatile("nop;nop;nop;nop;nop;nop\n\t" \ + "mova 9f,%0\n\t" \ + "sub %1,%0\n\t" \ + "jmp @r0 ! Back to P1 area\n\t" \ + " nop\n\t" \ + ".balign 4\n" \ + "9:" \ + : "=&z" (__dummy) \ + : "r" (0x20000000), "0" (__dummy)) + +/* Write back caches to memory (if needed) and invalidates the caches */ +void cache_flush_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v, p; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + p = __pa(v); + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* U=0, V=0 */ + ctrl_outl(data,addr); +#if CACHE_IC_ADDRESS_ARRAY != CACHE_OC_ADDRESS_ARRAY + asm volatile("ocbp %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* Purge (just invalidate, no write back) the caches */ +/* This is expected to work well.. but.. + + On SH7708S, the write-back cache is written back on "purge". + (it's not expected, though). + + It seems that we have no way to just purge (with no write back action) + the cache line. */ +void cache_purge_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v, p, j; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + p = __pa(v); + for (j=0; j<CACHE_IC_NUM_WAYS; j++) { + addr = CACHE_IC_ADDRESS_ARRAY|(j<<CACHE_IC_WAY_SHIFT)| + (v&CACHE_IC_ENTRY_MASK); + data = ctrl_inl(addr); + if ((data & 0xfffffc00) == (p&0xfffffc00) + && (data & CACHE_VALID)) { + data &= ~CACHE_VALID; + ctrl_outl(data,addr); + break; + } + } +#if CACHE_IC_ADDRESS_ARRAY != CACHE_OC_ADDRESS_ARRAY + asm volatile("ocbi %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* write back the dirty cache, but not invalidate the cache */ +void cache_wback_area(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { +#if CACHE_IC_ADDRESS_ARRAY == CACHE_OC_ADDRESS_ARRAY + unsigned long addr, data, j; + unsigned long p = __pa(v); + + for (j=0; j<CACHE_OC_NUM_WAYS; j++) { + addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| + (v&CACHE_OC_ENTRY_MASK); + data = ctrl_inl(addr); + if ((data & 0xfffffc00) == (p&0xfffffc00) + && (data & CACHE_VALID) + && (data & CACHE_UPDATED)) { + data &= ~CACHE_UPDATED; + ctrl_outl(data,addr); + break; + } + } +#else + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); +#endif + } + back_to_p1(__dummy); + restore_flags(flags); +} + +/* + * Write back the cache. + * + * For SH-4, flush (write back) Operand Cache, as Instruction Cache + * doesn't have "updated" data. + */ +static void cache_wback_all(void) +{ + unsigned long flags, __dummy; + unsigned long addr, data, i, j; + + save_and_cli(flags); + jump_to_p2(__dummy); + + for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { + for (j=0; j<CACHE_OC_NUM_WAYS; j++) { + addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| + (i<<CACHE_OC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if (data & CACHE_VALID) { + data &= ~(CACHE_VALID|CACHE_UPDATED); + ctrl_outl(data,addr); + } + } + } + + back_to_p1(__dummy); + restore_flags(flags); +} + +static void +detect_cpu_and_cache_system(void) +{ +#if defined(__sh3__) + unsigned long __dummy, addr0, addr1, data0, data1, data2, data3; + + jump_to_p2(__dummy); + /* Check if the entry shadows or not. + * When shadowed, it's 128-entry system. + * Otherwise, it's 256-entry system. + */ + addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12); + addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12); + data0 = ctrl_inl(addr0); + data0 ^= 0x00000001; + ctrl_outl(data0,addr0); + data1 = ctrl_inl(addr1); + data2 = data1 ^ 0x00000001; + ctrl_outl(data2,addr1); + data3 = ctrl_inl(addr0); + + /* Invaliate them, in case the cache has been enabled already. */ + ctrl_outl(data0&~0x00000001,addr0); + ctrl_outl(data2&~0x00000001,addr1); + back_to_p1(__dummy); + + if (data0 == data1 && data2 == data3) { /* Shadow */ + cache_system_info.way_shift = 11; + cache_system_info.entry_mask = 0x7f0; + cache_system_info.num_entries = 128; + cpu_data->type = CPU_SH7708; + } else { /* 7709A or 7729 */ + cache_system_info.way_shift = 12; + cache_system_info.entry_mask = 0xff0; + cache_system_info.num_entries = 256; + cpu_data->type = CPU_SH7729; + } +#elif defined(__SH4__) + cpu_data->type = CPU_SH7750; +#endif +} + +void __init cache_init(void) +{ + unsigned long __dummy, ccr; + + detect_cpu_and_cache_system(); + + ccr = ctrl_inl(CCR); + if (ccr == CCR_CACHE_VAL) + return; + if (ccr & CCR_CACHE_ENABLE) + /* Should check RA here. If RA was 1, + we only need to flush the half of the caches. */ + cache_wback_all(); + + jump_to_p2(__dummy); + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_p1(__dummy); +} + +#if defined(__SH4__) +/* Write back data caches, and invalidates instructiin caches */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + /* Write back O Cache */ + asm volatile("ocbwb %0" + : /* no output */ + : "m" (__m(v))); + /* Invalidate I Cache */ + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* Valid=0 */ + ctrl_outl(data,addr); + } + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_all(void) +{ + unsigned long flags,__dummy; + + /* Write back Operand Cache */ + cache_wback_all (); + + /* Then, invalidate Instruction Cache and Operand Cache */ + save_and_cli(flags); + jump_to_p2(__dummy); + ctrl_outl(CCR_CACHE_INIT, CCR); + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_mm(struct mm_struct *mm) +{ + /* Is there any good way? */ + /* XXX: possibly call flush_cache_range for each vm area */ + flush_cache_all(); +} + +void flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long flags, __dummy; + unsigned long addr, data, v; + + start &= ~(L1_CACHE_BYTES-1); + save_and_cli(flags); + jump_to_p2(__dummy); + + for (v = start; v < end; v+=L1_CACHE_BYTES) { + addr = CACHE_IC_ADDRESS_ARRAY | + (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; + data = (v&0xfffffc00); /* Update=0, Valid=0 */ + ctrl_outl(data,addr); + addr = CACHE_OC_ADDRESS_ARRAY | + (v&CACHE_OC_ENTRY_MASK) | 0x8 /* A-bit */; + ctrl_outl(data,addr); + } + back_to_p1(__dummy); + restore_flags(flags); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) +{ + flush_cache_range(vma->vm_mm, addr, addr+PAGE_SIZE); +} + +void flush_page_to_ram(unsigned long page) +{ /* Page is in physical address */ + /* XXX: for the time being... */ + flush_cache_all(); +} +#endif |