From: David W. <dw...@in...> - 2001-07-04 12:32:24
|
#ifdefs confuse me. As there's almost nothing common between the SH3 and SH4 code in cache.c, it looks like it makes sense to split them into separate files. --- arch/sh/mm/Makefile.orig Wed Jul 4 13:09:07 2001 +++ arch/sh/mm/Makefile Wed Jul 4 13:09:45 2001 @@ -8,6 +8,9 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -obj-y := init.o fault.o extable.o cache.o ioremap.o +obj-y := init.o fault.o extable.o ioremap.o + +obj-$(CONFIG_CPU_SH4) += cache-sh4.o +obj-$(CONFIG_CPU_SH3) += cache-sh3.o include $(TOPDIR)/Rules.make --- arch/sh/mm/cache-sh3.c.orig Wed Jul 4 13:09:24 2001 +++ arch/sh/mm/cache-sh3.c Wed Jul 4 13:04:25 2001 @@ -0,0 +1,143 @@ +/* $Id$ + * + * linux/arch/sh/mm/cache-sh3.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/threads.h> +#include <asm/addrspace.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> + +#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 + +/* + * 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. + * Should be INLINE function. + */ +static inline void cache_wback_all(void) +{ + unsigned long addr, data, i, j; + + 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_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID)) + ctrl_outl(data & ~CACHE_UPDATED, addr); + } + } +} + +static void __init +detect_cpu_and_cache_system(void) +{ + unsigned long addr0, addr1, data0, data1, data2, data3; + + jump_to_P2(); + /* + * 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); + + /* First, write back & invalidate */ + data0 = ctrl_inl(addr0); + ctrl_outl(data0&~(CACHE_VALID|CACHE_UPDATED), addr0); + data1 = ctrl_inl(addr1); + ctrl_outl(data1&~(CACHE_VALID|CACHE_UPDATED), addr1); + + /* Next, check if there's shadow or not */ + data0 = ctrl_inl(addr0); + data0 ^= CACHE_VALID; + ctrl_outl(data0, addr0); + data1 = ctrl_inl(addr1); + data2 = data1 ^ CACHE_VALID; + ctrl_outl(data2, addr1); + data3 = ctrl_inl(addr0); + + /* Lastly, invaliate them. */ + ctrl_outl(data0&~CACHE_VALID, addr0); + ctrl_outl(data1&~CACHE_VALID, addr1); + back_to_P1(); + + 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; + } +} + +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(); +} --- arch/sh/mm/cache-sh4.c.orig Wed Jul 4 13:09:28 2001 +++ arch/sh/mm/cache-sh4.c Wed Jul 4 13:05:36 2001 @@ -0,0 +1,510 @@ +/* $Id$ + * + * linux/arch/sh/mm/cache-sh4.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/threads.h> +#include <asm/addrspace.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> + +#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 + + +/* + * 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; 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_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID)) + ctrl_outl(data & ~CACHE_UPDATED, addr); + } + } +} + +static void __init +detect_cpu_and_cache_system(void) +{ +#ifdef CONFIG_CPU_SUBTYPE_ST40STB1 + cpu_data->type = CPU_ST40STB1; +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) + cpu_data->type = CPU_SH7750; +#else +#error Unknown SH4 CPU type +#endif +#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(); +} + +/* + * SH-4 has virtually indexed and physically tagged cache. + */ + +void __init p3_cache_init(void) +{ + /* In ioremap.c */ + extern int remap_area_pages(unsigned long address, + unsigned long phys_addr, + unsigned long size, unsigned long flags); + + if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE)) + panic("p3_cachie_init failed."); +} + +/* + * Write back the dirty D-caches, but not invalidate them. + * + * START, END: Virtual Address (U0, P1, or P3) + */ +void __flush_dcache_region(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))); + } +} + +/* + * No write back please + */ +static void __flush_invalidate_region(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("ocbi %0" + : /* no output */ + : "m" (__m(v))); + } +} + +/* + * Write back the range of D-cache, and purge the I-cache. + * + * Called from kernel/module.c:sys_init_module and routine for a.out format. + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ + flush_cache_all(); +} + +/* + * 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 */ +} + +/* + * Writeback&Invalidate the D-cache of the page + * Invalidate the I-cache of the page, if needed + */ +void __flush_cache_page(unsigned long phys, int exec) +{ + unsigned long addr, data; + unsigned long flags; + + phys|=CACHE_VALID; + save_and_cli(flags); + jump_to_P2(); + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY; + addr < (CACHE_OC_ADDRESS_ARRAY + +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT)); + addr += (1<<CACHE_OC_ENTRY_SHIFT)) { + data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + + if (exec) + /* Loop all the I-cache */ + for (addr = CACHE_IC_ADDRESS_ARRAY; + addr < (CACHE_IC_ADDRESS_ARRAY + +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); + addr += (1<<CACHE_IC_ENTRY_SHIFT)) { + data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); + if (data == phys) + ctrl_outl(0, addr); + } + back_to_P1(); + restore_flags(flags); +} + +#if 0 +/* + * 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; i<CACHE_IC_NUM_ENTRIES; i++) { + addr = CACHE_IC_ADDRESS_ARRAY| (i<<CACHE_IC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { + data &= ~CACHE_VALID; + ctrl_outl(data, addr); + } + } + back_to_P1(); +} +#endif + +/* + * Write back & invalidate the I/D-cache of the page. + * (To avoid "alias" issues) + */ +void flush_dcache_page(struct page *pg) +{ + if (!pg->mapping + || pg->mapping->i_mmap || pg->mapping->i_mmap_shared) { + int exec; + unsigned long phys; + + if (!pg->mapping) + exec = 0; + else + exec = 1; + + /* Physical address of this page */ + phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + __flush_cache_page(phys, exec); + } else + set_bit(PG_dcache_dirty, &pg->flags); +} + +void flush_cache_all(void) +{ + unsigned long flags; + unsigned long addr; + + save_and_cli(flags); + jump_to_P2(); + + /* Loop all the D-cache */ + for (addr = CACHE_OC_ADDRESS_ARRAY; + addr < (CACHE_OC_ADDRESS_ARRAY + +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT)); + addr += (1<<CACHE_OC_ENTRY_SHIFT)) + ctrl_outl(0, addr); + + /* Loop all the I-cache */ + for (addr = CACHE_IC_ADDRESS_ARRAY; + addr < (CACHE_IC_ADDRESS_ARRAY + +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); + addr += (1<<CACHE_IC_ENTRY_SHIFT)) + ctrl_outl(0, addr); + + back_to_P1(); + 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(); +} + +/* + * Write back and invalidate D-caches. + * + * START, END: Virtual Address (U0 address) + * + * NOTE: We need to flush the _physical_ page entry. + * Flushing the cache lines for U0 only isn't enough. + * We need to flush for P1 too, which may contain aliases. + */ +void flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + /* + * We could call flush_cache_page for the pages of these range, + * but it's not efficient (scan the caches all the time...). + * + * We can't use A-bit magic, as there's the case we don't have + * valid entry on TLB. + */ + flush_cache_all(); +} + +/* + * Write back and invalidate I/D-caches for the page. + * + * ADDR: Virtual Address (U0 address) + */ +void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) +{ + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + pte_t entry; + unsigned long phys; + + dir = pgd_offset(vma->vm_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 = PHYSADDR(pte_val(entry))&PAGE_MASK; + __flush_cache_page(phys, (vma->vm_flags & VM_EXEC)); +} + +#if 0 +/* + * 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. + */ +static 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_NUM_ENTRIES; i++) { + addr = CACHE_OC_ADDRESS_ARRAY| (i<<CACHE_OC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { + data &= ~(CACHE_UPDATED|CACHE_VALID); + ctrl_outl(data, addr); + } + } + back_to_P1(); +} + +void flush_page_to_ram(struct page *pg) +{ + unsigned long phys; + + /* Physical address of this page */ + phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + __flush_page_to_ram(phys_to_virt(phys)); +} +#endif + +/* + * Check entries of the I-cache & D-cache of the page. + * (To see "alias" issues) + */ +void check_cache_page(struct page *pg) +{ + unsigned long phys, addr, data, i; + unsigned long kaddr; + unsigned long cache_line_index; + int bingo = 0; + + /* Physical address of this page */ + phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + kaddr = phys + PAGE_OFFSET; + cache_line_index = (kaddr&CACHE_OC_ENTRY_MASK)>>CACHE_OC_ENTRY_SHIFT; + + jump_to_P2(); + /* Loop all the D-cache */ + for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { + addr = CACHE_OC_ADDRESS_ARRAY| (i<<CACHE_OC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if ((data & (CACHE_UPDATED|CACHE_VALID)) + == (CACHE_UPDATED|CACHE_VALID) + && (data&PAGE_MASK) == phys) { + data &= ~(CACHE_VALID|CACHE_UPDATED); + ctrl_outl(data, addr); + if ((i^cache_line_index)&0x180) + bingo = 1; + } + } + + cache_line_index &= 0xff; + /* Loop all the I-cache */ + for (i=0; i<CACHE_IC_NUM_ENTRIES; i++) { + addr = CACHE_IC_ADDRESS_ARRAY| (i<<CACHE_IC_ENTRY_SHIFT); + data = ctrl_inl(addr); + if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { + data &= ~CACHE_VALID; + ctrl_outl(data, addr); + if (((i^cache_line_index)&0x80)) + bingo = 2; + } + } + back_to_P1(); + + if (bingo) { + extern void dump_stack(void); + + if (bingo ==1) + printk("BINGO!\n"); + else + printk("Bingo!\n"); + dump_stack(); + printk("--------------------\n"); + } +} + +/* Page is 4K, OC size is 16K, there are four lines. */ +#define CACHE_ALIAS 0x00003000 + +/* + * clear_user_page + * @to: P1 address + * @address: U0 address to be mapped + */ +void clear_user_page(void *to, unsigned long address) +{ + if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) + clear_page(to); + else { + unsigned long flags; + pgprot_t pgprot = __pgprot(_PAGE_PRESENT | + _PAGE_RW | _PAGE_CACHABLE | + _PAGE_DIRTY | _PAGE_ACCESSED | + _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); + unsigned long phys_addr = PHYSADDR(to); + unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); + pgd_t *dir = pgd_offset_k(p3_addr); + pmd_t *pmd = pmd_offset(dir, p3_addr); + pte_t *pte = pte_offset(pmd, p3_addr); + pte_t entry; + + __flush_invalidate_region((unsigned long)to, + (unsigned long)to+PAGE_SIZE); + + save_and_cli(flags); + entry = mk_pte_phys(phys_addr, pgprot); + set_pte(pte, entry); + update_mmu_cache(NULL, p3_addr, entry); + clear_page((void *)p3_addr); + restore_flags(flags); + } +} + +/* + * copy_user_page + * @to: P1 address + * @from: P1 address + * @address: U0 address to be mapped + */ +void copy_user_page(void *to, void *from, unsigned long address) +{ + if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) + copy_page(to, from); + else { + unsigned long flags; + pgprot_t pgprot = __pgprot(_PAGE_PRESENT | + _PAGE_RW | _PAGE_CACHABLE | + _PAGE_DIRTY | _PAGE_ACCESSED | + _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); + unsigned long phys_addr = PHYSADDR(to); + unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); + pgd_t *dir = pgd_offset_k(p3_addr); + pmd_t *pmd = pmd_offset(dir, p3_addr); + pte_t *pte = pte_offset(pmd, p3_addr); + pte_t entry; + + __flush_invalidate_region((unsigned long)to, + (unsigned long)to+PAGE_SIZE); + + save_and_cli(flags); + entry = mk_pte_phys(phys_addr, pgprot); + set_pte(pte, entry); + update_mmu_cache(NULL, p3_addr, entry); + copy_page((void *)p3_addr, from); + restore_flags(flags); + } +} --- arch/sh/mm/cache.c.orig Wed Jul 4 12:56:49 2001 +++ arch/sh/mm/cache.c Wed Jul 4 13:09:40 2001 @@ -1,590 +0,0 @@ -/* $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 <linux/config.h> -#include <linux/init.h> -#include <linux/mman.h> -#include <linux/mm.h> -#include <linux/threads.h> -#include <asm/addrspace.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; -}; - -/* 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; 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_UPDATED|CACHE_VALID)) - == (CACHE_UPDATED|CACHE_VALID)) - ctrl_outl(data & ~CACHE_UPDATED, addr); - } - } -} - -static void __init -detect_cpu_and_cache_system(void) -{ -#if defined(__sh3__) - unsigned long addr0, addr1, data0, data1, data2, data3; - - jump_to_P2(); - /* - * 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); - - /* First, write back & invalidate */ - data0 = ctrl_inl(addr0); - ctrl_outl(data0&~(CACHE_VALID|CACHE_UPDATED), addr0); - data1 = ctrl_inl(addr1); - ctrl_outl(data1&~(CACHE_VALID|CACHE_UPDATED), addr1); - - /* Next, check if there's shadow or not */ - data0 = ctrl_inl(addr0); - data0 ^= CACHE_VALID; - ctrl_outl(data0, addr0); - data1 = ctrl_inl(addr1); - data2 = data1 ^ CACHE_VALID; - ctrl_outl(data2, addr1); - data3 = ctrl_inl(addr0); - - /* Lastly, invaliate them. */ - ctrl_outl(data0&~CACHE_VALID, addr0); - ctrl_outl(data1&~CACHE_VALID, addr1); - back_to_P1(); - - 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__) -#ifdef CONFIG_CPU_SUBTYPE_ST40STB1 - cpu_data->type = CPU_ST40STB1; -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751) - cpu_data->type = CPU_SH7750; -#else -#error Unknown SH4 CPU type -#endif -#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. - */ - -void __init p3_cache_init(void) -{ - /* In ioremap.c */ - extern int remap_area_pages(unsigned long address, - unsigned long phys_addr, - unsigned long size, unsigned long flags); - - if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE)) - panic("p3_cachie_init failed."); -} - -/* - * Write back the dirty D-caches, but not invalidate them. - * - * START, END: Virtual Address (U0, P1, or P3) - */ -void __flush_dcache_region(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))); - } -} - -/* - * No write back please - */ -static void __flush_invalidate_region(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("ocbi %0" - : /* no output */ - : "m" (__m(v))); - } -} - -/* - * Write back the range of D-cache, and purge the I-cache. - * - * Called from kernel/module.c:sys_init_module and routine for a.out format. - */ -void flush_icache_range(unsigned long start, unsigned long end) -{ - flush_cache_all(); -} - -/* - * 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 */ -} - -/* - * Writeback&Invalidate the D-cache of the page - * Invalidate the I-cache of the page, if needed - */ -void __flush_cache_page(unsigned long phys, int exec) -{ - unsigned long addr, data; - unsigned long flags; - - phys|=CACHE_VALID; - save_and_cli(flags); - jump_to_P2(); - /* Loop all the D-cache */ - for (addr = CACHE_OC_ADDRESS_ARRAY; - addr < (CACHE_OC_ADDRESS_ARRAY - +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT)); - addr += (1<<CACHE_OC_ENTRY_SHIFT)) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - - if (exec) - /* Loop all the I-cache */ - for (addr = CACHE_IC_ADDRESS_ARRAY; - addr < (CACHE_IC_ADDRESS_ARRAY - +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); - addr += (1<<CACHE_IC_ENTRY_SHIFT)) { - data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID); - if (data == phys) - ctrl_outl(0, addr); - } - back_to_P1(); - restore_flags(flags); -} - -#if 0 -/* - * 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; i<CACHE_IC_NUM_ENTRIES; i++) { - addr = CACHE_IC_ADDRESS_ARRAY| (i<<CACHE_IC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { - data &= ~CACHE_VALID; - ctrl_outl(data, addr); - } - } - back_to_P1(); -} -#endif - -/* - * Write back & invalidate the I/D-cache of the page. - * (To avoid "alias" issues) - */ -void flush_dcache_page(struct page *pg) -{ - if (!pg->mapping - || pg->mapping->i_mmap || pg->mapping->i_mmap_shared) { - int exec; - unsigned long phys; - - if (!pg->mapping) - exec = 0; - else - exec = 1; - - /* Physical address of this page */ - phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; - __flush_cache_page(phys, exec); - } else - set_bit(PG_dcache_dirty, &pg->flags); -} - -void flush_cache_all(void) -{ - unsigned long flags; - unsigned long addr; - - save_and_cli(flags); - jump_to_P2(); - - /* Loop all the D-cache */ - for (addr = CACHE_OC_ADDRESS_ARRAY; - addr < (CACHE_OC_ADDRESS_ARRAY - +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT)); - addr += (1<<CACHE_OC_ENTRY_SHIFT)) - ctrl_outl(0, addr); - - /* Loop all the I-cache */ - for (addr = CACHE_IC_ADDRESS_ARRAY; - addr < (CACHE_IC_ADDRESS_ARRAY - +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT)); - addr += (1<<CACHE_IC_ENTRY_SHIFT)) - ctrl_outl(0, addr); - - back_to_P1(); - 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(); -} - -/* - * Write back and invalidate D-caches. - * - * START, END: Virtual Address (U0 address) - * - * NOTE: We need to flush the _physical_ page entry. - * Flushing the cache lines for U0 only isn't enough. - * We need to flush for P1 too, which may contain aliases. - */ -void flush_cache_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - /* - * We could call flush_cache_page for the pages of these range, - * but it's not efficient (scan the caches all the time...). - * - * We can't use A-bit magic, as there's the case we don't have - * valid entry on TLB. - */ - flush_cache_all(); -} - -/* - * Write back and invalidate I/D-caches for the page. - * - * ADDR: Virtual Address (U0 address) - */ -void flush_cache_page(struct vm_area_struct *vma, unsigned long addr) -{ - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - pte_t entry; - unsigned long phys; - - dir = pgd_offset(vma->vm_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 = PHYSADDR(pte_val(entry))&PAGE_MASK; - __flush_cache_page(phys, (vma->vm_flags & VM_EXEC)); -} - -#if 0 -/* - * 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. - */ -static 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_NUM_ENTRIES; i++) { - addr = CACHE_OC_ADDRESS_ARRAY| (i<<CACHE_OC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { - data &= ~(CACHE_UPDATED|CACHE_VALID); - ctrl_outl(data, addr); - } - } - back_to_P1(); -} - -void flush_page_to_ram(struct page *pg) -{ - unsigned long phys; - - /* Physical address of this page */ - phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; - __flush_page_to_ram(phys_to_virt(phys)); -} -#endif - -/* - * Check entries of the I-cache & D-cache of the page. - * (To see "alias" issues) - */ -void check_cache_page(struct page *pg) -{ - unsigned long phys, addr, data, i; - unsigned long kaddr; - unsigned long cache_line_index; - int bingo = 0; - - /* Physical address of this page */ - phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; - kaddr = phys + PAGE_OFFSET; - cache_line_index = (kaddr&CACHE_OC_ENTRY_MASK)>>CACHE_OC_ENTRY_SHIFT; - - jump_to_P2(); - /* Loop all the D-cache */ - for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { - addr = CACHE_OC_ADDRESS_ARRAY| (i<<CACHE_OC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & (CACHE_UPDATED|CACHE_VALID)) - == (CACHE_UPDATED|CACHE_VALID) - && (data&PAGE_MASK) == phys) { - data &= ~(CACHE_VALID|CACHE_UPDATED); - ctrl_outl(data, addr); - if ((i^cache_line_index)&0x180) - bingo = 1; - } - } - - cache_line_index &= 0xff; - /* Loop all the I-cache */ - for (i=0; i<CACHE_IC_NUM_ENTRIES; i++) { - addr = CACHE_IC_ADDRESS_ARRAY| (i<<CACHE_IC_ENTRY_SHIFT); - data = ctrl_inl(addr); - if ((data & CACHE_VALID) && (data&PAGE_MASK) == phys) { - data &= ~CACHE_VALID; - ctrl_outl(data, addr); - if (((i^cache_line_index)&0x80)) - bingo = 2; - } - } - back_to_P1(); - - if (bingo) { - extern void dump_stack(void); - - if (bingo ==1) - printk("BINGO!\n"); - else - printk("Bingo!\n"); - dump_stack(); - printk("--------------------\n"); - } -} - -/* Page is 4K, OC size is 16K, there are four lines. */ -#define CACHE_ALIAS 0x00003000 - -/* - * clear_user_page - * @to: P1 address - * @address: U0 address to be mapped - */ -void clear_user_page(void *to, unsigned long address) -{ - if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) - clear_page(to); - else { - unsigned long flags; - pgprot_t pgprot = __pgprot(_PAGE_PRESENT | - _PAGE_RW | _PAGE_CACHABLE | - _PAGE_DIRTY | _PAGE_ACCESSED | - _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); - unsigned long phys_addr = PHYSADDR(to); - unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); - pgd_t *dir = pgd_offset_k(p3_addr); - pmd_t *pmd = pmd_offset(dir, p3_addr); - pte_t *pte = pte_offset(pmd, p3_addr); - pte_t entry; - - __flush_invalidate_region((unsigned long)to, - (unsigned long)to+PAGE_SIZE); - - save_and_cli(flags); - entry = mk_pte_phys(phys_addr, pgprot); - set_pte(pte, entry); - update_mmu_cache(NULL, p3_addr, entry); - clear_page((void *)p3_addr); - restore_flags(flags); - } -} - -/* - * copy_user_page - * @to: P1 address - * @from: P1 address - * @address: U0 address to be mapped - */ -void copy_user_page(void *to, void *from, unsigned long address) -{ - if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) - copy_page(to, from); - else { - unsigned long flags; - pgprot_t pgprot = __pgprot(_PAGE_PRESENT | - _PAGE_RW | _PAGE_CACHABLE | - _PAGE_DIRTY | _PAGE_ACCESSED | - _PAGE_HW_SHARED | _PAGE_FLAGS_HARD); - unsigned long phys_addr = PHYSADDR(to); - unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS); - pgd_t *dir = pgd_offset_k(p3_addr); - pmd_t *pmd = pmd_offset(dir, p3_addr); - pte_t *pte = pte_offset(pmd, p3_addr); - pte_t entry; - - __flush_invalidate_region((unsigned long)to, - (unsigned long)to+PAGE_SIZE); - - save_and_cli(flags); - entry = mk_pte_phys(phys_addr, pgprot); - set_pte(pte, entry); - update_mmu_cache(NULL, p3_addr, entry); - copy_page((void *)p3_addr, from); - restore_flags(flags); - } -} -#endif -- dwmw2 |