1/* $Id: cache-sh3.c,v 1.1.1.1 2008/10/15 03:26:18 james26_jang Exp $ 2 * 3 * linux/arch/sh/mm/cache-sh3.c 4 * 5 * Copyright (C) 1999, 2000 Niibe Yutaka 6 * 7 */ 8 9#include <linux/init.h> 10#include <linux/mman.h> 11#include <linux/mm.h> 12#include <linux/threads.h> 13#include <asm/addrspace.h> 14#include <asm/page.h> 15#include <asm/pgtable.h> 16#include <asm/processor.h> 17#include <asm/cache.h> 18#include <asm/io.h> 19#include <asm/uaccess.h> 20#include <asm/pgalloc.h> 21#include <asm/mmu_context.h> 22 23 24#define CCR 0xffffffec /* Address of Cache Control Register */ 25 26#define CCR_CACHE_CE 0x01 /* Cache Enable */ 27#define CCR_CACHE_WT 0x02 /* Write-Through (for P0,U0,P3) (else writeback) */ 28#define CCR_CACHE_CB 0x04 /* Write-Back (for P1) (else writethrough) */ 29#define CCR_CACHE_CF 0x08 /* Cache Flush */ 30#define CCR_CACHE_RA 0x20 /* RAM mode */ 31 32#define CCR_CACHE_VAL (CCR_CACHE_CB|CCR_CACHE_CE) /* 8k-byte cache, P1-wb, enable */ 33#define CCR_CACHE_INIT (CCR_CACHE_CF|CCR_CACHE_VAL) /* 8k-byte cache, CF, P1-wb, enable */ 34 35#define CACHE_OC_ADDRESS_ARRAY 0xf0000000 36#define CACHE_VALID 1 37#define CACHE_UPDATED 2 38#define CACHE_PHYSADDR_MASK 0x1ffffc00 39 40/* 7709A/7729 has 16K cache (256-entry), while 7702 has only 2K(direct) 41 7702 is not supported (yet) */ 42struct _cache_system_info { 43 int way_shift; 44 int entry_mask; 45 int num_entries; 46}; 47 48/* Data at BSS is cleared after setting this variable. 49 So, we Should not placed this variable at BSS section. 50 Initialize this, it is placed at data section. */ 51static struct _cache_system_info cache_system_info = {0,}; 52 53#define CACHE_OC_WAY_SHIFT (cache_system_info.way_shift) 54#define CACHE_OC_ENTRY_SHIFT 4 55#define CACHE_OC_ENTRY_MASK (cache_system_info.entry_mask) 56#define CACHE_OC_NUM_ENTRIES (cache_system_info.num_entries) 57#define CACHE_OC_NUM_WAYS 4 58#define CACHE_OC_ASSOC_BIT (1<<3) 59 60 61/* 62 * Write back all the cache. 63 * 64 * For SH-4, we only need to flush (write back) Operand Cache, 65 * as Instruction Cache doesn't have "updated" data. 66 * 67 * Assumes that this is called in interrupt disabled context, and P2. 68 * Shuld be INLINE function. 69 */ 70static inline void cache_wback_all(void) 71{ 72 unsigned long addr, data, i, j; 73 74 for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { 75 for (j=0; j<CACHE_OC_NUM_WAYS; j++) { 76 addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| 77 (i<<CACHE_OC_ENTRY_SHIFT); 78 data = ctrl_inl(addr); 79 if ((data & (CACHE_UPDATED|CACHE_VALID)) 80 == (CACHE_UPDATED|CACHE_VALID)) 81 ctrl_outl(data & ~CACHE_UPDATED, addr); 82 } 83 } 84} 85 86static void __init 87detect_cpu_and_cache_system(void) 88{ 89 unsigned long addr0, addr1, data0, data1, data2, data3; 90 91 jump_to_P2(); 92 /* 93 * Check if the entry shadows or not. 94 * When shadowed, it's 128-entry system. 95 * Otherwise, it's 256-entry system. 96 */ 97 addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12); 98 addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12); 99 100 /* First, write back & invalidate */ 101 data0 = ctrl_inl(addr0); 102 ctrl_outl(data0&~(CACHE_VALID|CACHE_UPDATED), addr0); 103 data1 = ctrl_inl(addr1); 104 ctrl_outl(data1&~(CACHE_VALID|CACHE_UPDATED), addr1); 105 106 /* Next, check if there's shadow or not */ 107 data0 = ctrl_inl(addr0); 108 data0 ^= CACHE_VALID; 109 ctrl_outl(data0, addr0); 110 data1 = ctrl_inl(addr1); 111 data2 = data1 ^ CACHE_VALID; 112 ctrl_outl(data2, addr1); 113 data3 = ctrl_inl(addr0); 114 115 /* Lastly, invaliate them. */ 116 ctrl_outl(data0&~CACHE_VALID, addr0); 117 ctrl_outl(data2&~CACHE_VALID, addr1); 118 back_to_P1(); 119 120 if (data0 == data1 && data2 == data3) { /* Shadow */ 121 cache_system_info.way_shift = 11; 122 cache_system_info.entry_mask = 0x7f0; 123 cache_system_info.num_entries = 128; 124 cpu_data->type = CPU_SH7708; 125 } else { /* 7709A or 7729 */ 126 cache_system_info.way_shift = 12; 127 cache_system_info.entry_mask = 0xff0; 128 cache_system_info.num_entries = 256; 129 cpu_data->type = CPU_SH7729; 130 } 131} 132 133void __init cache_init(void) 134{ 135 unsigned long ccr; 136 137 detect_cpu_and_cache_system(); 138 139 jump_to_P2(); 140 ccr = ctrl_inl(CCR); 141 if (ccr & CCR_CACHE_CE) 142 cache_wback_all(); 143 144 ctrl_outl(CCR_CACHE_INIT, CCR); 145 back_to_P1(); 146} 147 148/* 149 * Write back the dirty D-caches, but not invalidate them. 150 * 151 * Is this really worth it, or should we just alias this routine 152 * to __flush_purge_region too? 153 * 154 * START: Virtual Address (U0, P1, or P3) 155 * SIZE: Size of the region. 156 */ 157 158void __flush_wback_region(void *start, int size) 159{ 160 unsigned long v, j; 161 unsigned long begin, end; 162 unsigned long flags; 163 164 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); 165 end = ((unsigned long)start + size + L1_CACHE_BYTES-1) 166 & ~(L1_CACHE_BYTES-1); 167 168 for (v = begin; v < end; v+=L1_CACHE_BYTES) { 169 for (j=0; j<CACHE_OC_NUM_WAYS; j++) { 170 unsigned long data, addr, p; 171 172 p = __pa(v); 173 addr = CACHE_OC_ADDRESS_ARRAY|(j<<CACHE_OC_WAY_SHIFT)| 174 (v&CACHE_OC_ENTRY_MASK); 175 save_and_cli(flags); 176 data = ctrl_inl(addr); 177 178 if ((data & CACHE_PHYSADDR_MASK) == 179 (p & CACHE_PHYSADDR_MASK)) { 180 data &= ~CACHE_UPDATED; 181 ctrl_outl(data, addr); 182 restore_flags(flags); 183 break; 184 } 185 restore_flags(flags); 186 } 187 } 188} 189 190/* 191 * Write back the dirty D-caches and invalidate them. 192 * 193 * START: Virtual Address (U0, P1, or P3) 194 * SIZE: Size of the region. 195 */ 196void __flush_purge_region(void *start, int size) 197{ 198 unsigned long v; 199 unsigned long begin, end; 200 201 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); 202 end = ((unsigned long)start + size + L1_CACHE_BYTES-1) 203 & ~(L1_CACHE_BYTES-1); 204 205 for (v = begin; v < end; v+=L1_CACHE_BYTES) { 206 unsigned long data, addr; 207 208 data = (v & 0xfffffc00); /* _Virtual_ address, ~U, ~V */ 209 addr = CACHE_OC_ADDRESS_ARRAY | (v&CACHE_OC_ENTRY_MASK) | 210 CACHE_OC_ASSOC_BIT; 211 ctrl_outl(data, addr); 212 } 213} 214 215/* 216 * No write back please 217 * 218 * Except I don't think there's any way to avoid the writeback. So we 219 * just alias it to __flush_purge_region(). dwmw2. 220 */ 221void __flush_invalidate_region(void *start, int size) 222 __attribute__((alias("__flush_purge_region"))); 223