patch-2.3.23 linux/arch/sh/mm/cache.c
Next file: linux/arch/sh/mm/extable.c
Previous file: linux/arch/sh/mm/Makefile
Back to the patch index
Back to the overall index
- Lines: 367
- Date:
Mon Oct 18 11:16:13 1999
- Orig file:
v2.3.22/linux/arch/sh/mm/cache.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.22/linux/arch/sh/mm/cache.c linux/arch/sh/mm/cache.c
@@ -0,0 +1,366 @@
+/* $Id: cache.c,v 1.7 1999/09/23 11:43:07 gniibe Exp $
+ *
+ * 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
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)