/* $Id: cache.c,v 1.10 2000/03/07 11:58:34 gniibe Exp $ * * linux/arch/sh/mm/cache.c * * Copyright (C) 1999, 2000 Niibe Yutaka * */ #include #include #include #include #include #include #include #include #include #include #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; }; /* Data at BSS is cleared after setting this variable. So, we Should not placed this variable at BSS section. Initialize this, it is placed at data section. */ static struct _cache_system_info cache_system_info = {0,}; #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 /* ICI,ICE(8k), OCI,P1-wb,OCE(16k) */ #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_IC_ENTRY_SHIFT 5 #define CACHE_OC_ENTRY_MASK 0x3fe0 #define CACHE_OC_ENTRY_PHYS_MASK 0x0fe0 #define CACHE_IC_ENTRY_MASK 0x1fe0 #define CACHE_IC_NUM_ENTRIES 256 #define CACHE_OC_NUM_ENTRIES 512 #define CACHE_OC_NUM_WAYS 1 #define CACHE_IC_NUM_WAYS 1 #endif /* * Write back all the cache. * * For SH-4, we only need to flush (write back) Operand Cache, * as Instruction Cache doesn't have "updated" data. * * Assumes that this is called in interrupt disabled context, and P2. * Shuld be INLINE function. */ static inline void cache_wback_all(void) { unsigned long addr, data, i, j; for (i=0; itype = 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 ccr; detect_cpu_and_cache_system(); ccr = ctrl_inl(CCR); jump_to_P2(); if (ccr & CCR_CACHE_ENABLE) /* * XXX: Should check RA here. * If RA was 1, we only need to flush the half of the caches. */ cache_wback_all(); ctrl_outl(CCR_CACHE_INIT, CCR); back_to_P1(); } #if defined(__SH4__) /* * SH-4 has virtually indexed and physically tagged cache. */ /* * Write back the dirty D-caches, but not invalidate them. * * START, END: Virtual Address */ static void dcache_wback_range(unsigned long start, unsigned long end) { unsigned long v; start &= ~(L1_CACHE_BYTES-1); for (v = start; v < end; v+=L1_CACHE_BYTES) { asm volatile("ocbwb %0" : /* no output */ : "m" (__m(v))); } } /* * Invalidate I-caches. * * START, END: Virtual Address * */ static void icache_purge_range(unsigned long start, unsigned long end) { unsigned long addr, data, v; start &= ~(L1_CACHE_BYTES-1); jump_to_P2(); /* * To handle the cache-line, we calculate the entry with virtual * address: entry = vaddr & CACHE_IC_ENTRY_MASK. * * With A-bit "on", data written to is translated by MMU and * compared the tag of cache and if it's not matched, nothing * will be occurred. (We can avoid flushing other caches.) * * NOTE: We can use A-bit feature here, because we have valid * entriy in TLB (at least in UTLB), as dcache_wback_range is * called before this function is called. */ 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); /* Valid=0 */ ctrl_outl(data, addr); } back_to_P1(); } /* * Write back the range of D-cache, and purge the I-cache. * * Called from sh/kernel/signal.c, after accessing the memory * through U0 area. START and END is the address of U0. */ void flush_icache_range(unsigned long start, unsigned long end) { unsigned long flags; save_and_cli(flags); dcache_wback_range(start, end); icache_purge_range(start, end); restore_flags(flags); } /* * Write back the D-cache and purge the I-cache for signal trampoline. */ void flush_cache_sigtramp(unsigned long addr) { unsigned long v, index; v = addr & ~(L1_CACHE_BYTES-1); asm volatile("ocbwb %0" : /* no output */ : "m" (__m(v))); index = CACHE_IC_ADDRESS_ARRAY| (v&CACHE_IC_ENTRY_MASK); ctrl_outl(0, index); /* Clear out Valid-bit */ } /* * Invalidate the I-cache of the page (don't need to write back D-cache). * * Called from kernel/ptrace.c, mm/memory.c after flush_page_to_ram is called. */ void flush_icache_page(struct vm_area_struct *vma, struct page *pg) { unsigned long phys, addr, data, i; /* Physical address of this page */ phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; jump_to_P2(); /* Loop all the I-cache */ for (i=0; ivm_mm, addr); pmd = pmd_offset(dir, addr); if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) return; pte = pte_offset(pmd, addr); entry = *pte; if (pte_none(entry) || !pte_present(entry)) return; phys = pte_val(entry)&PAGE_MASK; pg = virt_to_page(__va(phys)); flush_dcache_page(pg); } /* * Write-back & invalidate the cache. * * After accessing the memory from kernel space (P1-area), we need to * write back the cache line. * * We search the D-cache to see if we have the entries corresponding to * the page, and if found, write back them. */ void __flush_page_to_ram(void *kaddr) { unsigned long phys, addr, data, i; /* Physical address of this page */ phys = PHYSADDR(kaddr); jump_to_P2(); /* Loop all the D-cache */ for (i=0; i>CACHE_OC_ENTRY_SHIFT; jump_to_P2(); /* Loop all the D-cache */ for (i=0; i