Update of /cvsroot/linux-mips/linux/arch/mips/mm In directory usw-pr-cvs1:/tmp/cvs-serv13923/mm Modified Files: Makefile loadmmu.c pg-mips32.c pg-r3k.c pg-r4k.S pg-r5432.c pg-rm7k.c tlb-r3k.c Added Files: c-andes.c c-mips32.c c-r3k.c c-r4k.c c-r5432.c c-rm7k.c c-sb1.c c-tx39.c pg-andes.S pg-sb1.c Removed Files: andes.c mips32.c pg-andes.c r2300.c r4xx0.c r5432.c rm7k.c sb1.c Log Message: More berzerking in the cache code. --- NEW FILE: c-andes.c --- /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1997, 1998, 1999 Ralf Baechle (ra...@gn...) * Copyright (C) 1999 Silicon Graphics, Inc. * Copyright (C) 2000 Kanoj Sarcar (ka...@sg...) */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/r10kcache.h> #include <asm/system.h> #include <asm/sgialib.h> #include <asm/mmu_context.h> static int scache_lsz64; static void andes_flush_cache_all(void) { } static void andes_flush_cache_mm(struct mm_struct *mm) { } static void andes_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { } static void andes_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { } static void andes_flush_page_to_ram(struct page *page) { } /* Cache operations. These are only used with the virtual memory system, not for non-coherent I/O so it's ok to ignore the secondary caches. */ static void andes_flush_cache_l1(void) { blast_dcache32(); blast_icache64(); } /* * This is only used during initialization time. vmalloc() also calls * this, but that will be changed pretty soon. */ static void andes_flush_cache_l2(void) { switch (sc_lsize()) { case 64: blast_scache64(); break; case 128: blast_scache128(); break; default: printk("Unknown L2 line size\n"); while(1); } } void andes_flush_icache_page(unsigned long page) { if (scache_lsz64) blast_scache64_page(page); else blast_scache128_page(page); } static void andes_flush_cache_sigtramp(unsigned long addr) { protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); protected_flush_icache_line(addr & ~(ic_lsize - 1)); } void __init ld_mmu_andes(void) { printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); printk("Primary instruction cache %dkb, linesize %d bytes\n", icache_size >> 10, ic_lsize); printk("Primary data cache %dkb, linesize %d bytes\n", dcache_size >> 10, dc_lsize); printk("Secondary cache sized at %ldK, linesize %ld\n", scache_size() >> 10, sc_lsize()); _clear_page = andes_clear_page; _copy_page = andes_copy_page; _flush_cache_all = andes_flush_cache_all; _flush_cache_mm = andes_flush_cache_mm; _flush_cache_page = andes_flush_cache_page; _flush_page_to_ram = andes_flush_page_to_ram; _flush_cache_l1 = andes_flush_cache_l1; _flush_cache_l2 = andes_flush_cache_l2; _flush_cache_sigtramp = andes_flush_cache_sigtramp; switch (sc_lsize()) { case 64: scache_lsz64 = 1; break; case 128: scache_lsz64 = 0; break; default: printk("Unknown L2 line size\n"); while(1); } update_mmu_cache = andes_update_mmu_cache; flush_cache_l1(); } --- NEW FILE: c-mips32.c --- /* * Kevin D. Kissell, ke...@mi... and Carsten Langgaard, car...@mi... * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * MIPS32 CPU variant specific MMU/Cache routines. */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/bootinfo.h> #include <asm/cpu.h> #include <asm/bcache.h> #include <asm/io.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/mmu_context.h> /* CP0 hazard avoidance. */ #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ "nop; nop; nop; nop; nop; nop;\n\t" \ ".set reorder\n\t") /* Primary cache parameters. */ int icache_size, dcache_size; /* Size in bytes */ int ic_lsize, dc_lsize; /* LineSize in bytes */ /* Secondary cache (if present) parameters. */ unsigned int scache_size, sc_lsize; /* Again, in bytes */ #include <asm/cacheops.h> #include <asm/mips32_cache.h> #undef DEBUG_CACHE /* * Dummy cache handling routines for machines without boardcaches */ static void no_sc_noop(void) {} static struct bcache_ops no_sc_ops = { (void *)no_sc_noop, (void *)no_sc_noop, (void *)no_sc_noop, (void *)no_sc_noop }; struct bcache_ops *bcops = &no_sc_ops; static inline void mips32_flush_cache_all_sc(void) { unsigned long flags; __save_and_cli(flags); blast_dcache(); blast_icache(); blast_scache(); __restore_flags(flags); } static inline void mips32_flush_cache_all_pc(void) { unsigned long flags; __save_and_cli(flags); blast_dcache(); blast_icache(); __restore_flags(flags); } static void mips32_flush_cache_range_sc(struct mm_struct *mm, unsigned long start, unsigned long end) { struct vm_area_struct *vma; unsigned long flags; if(mm->context == 0) return; start &= PAGE_MASK; #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif vma = find_vma(mm, start); if(vma) { if(mm->context != current->mm->context) { mips32_flush_cache_all_sc(); } else { pgd_t *pgd; pmd_t *pmd; pte_t *pte; __save_and_cli(flags); while(start < end) { pgd = pgd_offset(mm, start); pmd = pmd_offset(pgd, start); pte = pte_offset(pmd, start); if(pte_val(*pte) & _PAGE_VALID) blast_scache_page(start); start += PAGE_SIZE; } __restore_flags(flags); } } } static void mips32_flush_cache_range_pc(struct mm_struct *mm, unsigned long start, unsigned long end) { if(mm->context != 0) { unsigned long flags; #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif __save_and_cli(flags); blast_dcache(); blast_icache(); __restore_flags(flags); } } /* * On architectures like the Sparc, we could get rid of lines in * the cache created only by a certain context, but on the MIPS * (and actually certain Sparc's) we cannot. */ static void mips32_flush_cache_mm_sc(struct mm_struct *mm) { if(mm->context != 0) { #ifdef DEBUG_CACHE printk("cmm[%d]", (int)mm->context); #endif mips32_flush_cache_all_sc(); } } static void mips32_flush_cache_mm_pc(struct mm_struct *mm) { if(mm->context != 0) { #ifdef DEBUG_CACHE printk("cmm[%d]", (int)mm->context); #endif mips32_flush_cache_all_pc(); } } static void mips32_flush_cache_page_sc(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; unsigned long flags; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; /* * If ownes no valid ASID yet, cannot possibly have gotten * this page into the cache. */ if (mm->context == 0) return; #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif __save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); ptep = pte_offset(pmdp, page); /* * If the page isn't marked valid, the page cannot possibly be * in the cache. */ if (!(pte_val(*ptep) & _PAGE_VALID)) goto out; /* * Doing flushes for another ASID than the current one is * too difficult since R4k caches do a TLB translation * for every cache flush operation. So we do indexed flushes * in that case, which doesn't overly flush the cache too much. */ if (mm->context != current->active_mm->context) { /* * Do indexed flush, too much work to get the (possible) * tlb refills to work correctly. */ page = (KSEG0 + (page & (scache_size - 1))); blast_dcache_page_indexed(page); blast_scache_page_indexed(page); } else blast_scache_page(page); out: __restore_flags(flags); } static void mips32_flush_cache_page_pc(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; unsigned long flags; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; /* * If ownes no valid ASID yet, cannot possibly have gotten * this page into the cache. */ if (mm->context == 0) return; #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif __save_and_cli(flags); page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); ptep = pte_offset(pmdp, page); /* * If the page isn't marked valid, the page cannot possibly be * in the cache. */ if (!(pte_val(*ptep) & _PAGE_VALID)) goto out; /* * Doing flushes for another ASID than the current one is * too difficult since Mips32 caches do a TLB translation * for every cache flush operation. So we do indexed flushes * in that case, which doesn't overly flush the cache too much. */ if (mm == current->active_mm) { blast_dcache_page(page); } else { /* Do indexed flush, too much work to get the (possible) * tlb refills to work correctly. */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache_page_indexed(page); } out: __restore_flags(flags); } /* If the addresses passed to these routines are valid, they are * either: * * 1) In KSEG0, so we can do a direct flush of the page. * 2) In KSEG2, and since every process can translate those * addresses all the time in kernel mode we can do a direct * flush. * 3) In KSEG1, no flush necessary. */ static void mips32_flush_page_to_ram_sc(struct page *page) { blast_scache_page((unsigned long)page_address(page)); } static void mips32_flush_page_to_ram_pc(struct page *page) { blast_dcache_page((unsigned long)page_address(page)); } static void mips32_flush_icache_page_s(struct vm_area_struct *vma, struct page *page) { /* * We did an scache flush therefore PI is already clean. */ } static void mips32_flush_icache_range(unsigned long start, unsigned long end) { flush_cache_all(); } static void mips32_flush_icache_page(struct vm_area_struct *vma, struct page *page) { int address; if (!(vma->vm_flags & VM_EXEC)) return; address = KSEG0 + ((unsigned long)page_address(page) & PAGE_MASK & (dcache_size - 1)); blast_icache_page_indexed(address); } /* * Writeback and invalidate the primary cache dcache before DMA. */ static void mips32_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size) { unsigned long end, a; unsigned int flags; if (size >= dcache_size) { flush_cache_all(); } else { __save_and_cli(flags); a = addr & ~(dc_lsize - 1); end = (addr + size) & ~(dc_lsize - 1); while (1) { flush_dcache_line(a); /* Hit_Writeback_Inv_D */ if (a == end) break; a += dc_lsize; } __restore_flags(flags); } bc_wback_inv(addr, size); } static void mips32_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size) { unsigned long end, a; if (size >= scache_size) { flush_cache_all(); return; } a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { flush_scache_line(a); /* Hit_Writeback_Inv_SD */ if (a == end) break; a += sc_lsize; } } static void mips32_dma_cache_inv_pc(unsigned long addr, unsigned long size) { unsigned long end, a; unsigned int flags; if (size >= dcache_size) { flush_cache_all(); } else { __save_and_cli(flags); a = addr & ~(dc_lsize - 1); end = (addr + size) & ~(dc_lsize - 1); while (1) { flush_dcache_line(a); /* Hit_Writeback_Inv_D */ if (a == end) break; a += dc_lsize; } __restore_flags(flags); } bc_inv(addr, size); } static void mips32_dma_cache_inv_sc(unsigned long addr, unsigned long size) { unsigned long end, a; if (size >= scache_size) { flush_cache_all(); return; } a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { flush_scache_line(a); /* Hit_Writeback_Inv_SD */ if (a == end) break; a += sc_lsize; } } static void mips32_dma_cache_wback(unsigned long addr, unsigned long size) { panic("mips32_dma_cache called - should not happen.\n"); } /* * While we're protected against bad userland addresses we don't care * very much about what happens in that case. Usually a segmentation * fault will dump the process later on anyway ... */ static void mips32_flush_cache_sigtramp(unsigned long addr) { protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); protected_flush_icache_line(addr & ~(ic_lsize - 1)); } /* Detect and size the various caches. */ static void __init probe_icache(unsigned long config) { unsigned long config1; unsigned int lsize; if (!(config & (1 << 31))) { /* * Not a MIPS32 complainant CPU. * Config 1 register not supported, we assume R4k style. */ icache_size = 1 << (12 + ((config >> 9) & 7)); ic_lsize = 16 << ((config >> 5) & 1); mips_cpu.icache.linesz = ic_lsize; /* * We cannot infer associativity - assume direct map * unless probe template indicates otherwise */ if(!mips_cpu.icache.ways) mips_cpu.icache.ways = 1; mips_cpu.icache.sets = (icache_size / ic_lsize) / mips_cpu.icache.ways; } else { config1 = read_mips32_cp0_config1(); if ((lsize = ((config1 >> 19) & 7))) mips_cpu.icache.linesz = 2 << lsize; else mips_cpu.icache.linesz = lsize; mips_cpu.icache.sets = 64 << ((config1 >> 22) & 7); mips_cpu.icache.ways = 1 + ((config1 >> 16) & 7); ic_lsize = mips_cpu.icache.linesz; icache_size = mips_cpu.icache.sets * mips_cpu.icache.ways * ic_lsize; } printk("Primary instruction cache %dkb, linesize %d bytes (%d ways)\n", icache_size >> 10, ic_lsize, mips_cpu.icache.ways); } static void __init probe_dcache(unsigned long config) { unsigned long config1; unsigned int lsize; if (!(config & (1 << 31))) { /* * Not a MIPS32 complainant CPU. * Config 1 register not supported, we assume R4k style. */ dcache_size = 1 << (12 + ((config >> 6) & 7)); dc_lsize = 16 << ((config >> 4) & 1); mips_cpu.dcache.linesz = dc_lsize; /* * We cannot infer associativity - assume direct map * unless probe template indicates otherwise */ if(!mips_cpu.dcache.ways) mips_cpu.dcache.ways = 1; mips_cpu.dcache.sets = (dcache_size / dc_lsize) / mips_cpu.dcache.ways; } else { config1 = read_mips32_cp0_config1(); if ((lsize = ((config1 >> 10) & 7))) mips_cpu.dcache.linesz = 2 << lsize; else mips_cpu.dcache.linesz= lsize; mips_cpu.dcache.sets = 64 << ((config1 >> 13) & 7); mips_cpu.dcache.ways = 1 + ((config1 >> 7) & 7); dc_lsize = mips_cpu.dcache.linesz; dcache_size = mips_cpu.dcache.sets * mips_cpu.dcache.ways * dc_lsize; } printk("Primary data cache %dkb, linesize %d bytes (%d ways)\n", dcache_size >> 10, dc_lsize, mips_cpu.dcache.ways); } /* If you even _breathe_ on this function, look at the gcc output * and make sure it does not pop things on and off the stack for * the cache sizing loop that executes in KSEG1 space or else * you will crash and burn badly. You have been warned. */ static int __init probe_scache(unsigned long config) { extern unsigned long stext; unsigned long flags, addr, begin, end, pow2; int tmp; if (mips_cpu.scache.flags == MIPS_CACHE_NOT_PRESENT) return 0; tmp = ((config >> 17) & 1); if(tmp) return 0; tmp = ((config >> 22) & 3); switch(tmp) { case 0: sc_lsize = 16; break; case 1: sc_lsize = 32; break; case 2: sc_lsize = 64; break; case 3: sc_lsize = 128; break; } begin = (unsigned long) &stext; begin &= ~((4 * 1024 * 1024) - 1); end = begin + (4 * 1024 * 1024); /* This is such a bitch, you'd think they would make it * easy to do this. Away you daemons of stupidity! */ __save_and_cli(flags); /* Fill each size-multiple cache line with a valid tag. */ pow2 = (64 * 1024); for(addr = begin; addr < end; addr = (begin + pow2)) { unsigned long *p = (unsigned long *) addr; __asm__ __volatile__("nop" : : "r" (*p)); /* whee... */ pow2 <<= 1; } /* Load first line with zero (therefore invalid) tag. */ set_taglo(0); set_taghi(0); __asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */ __asm__ __volatile__("\n\t.set noreorder\n\t" ".set mips3\n\t" "cache 8, (%0)\n\t" ".set mips0\n\t" ".set reorder\n\t" : : "r" (begin)); __asm__ __volatile__("\n\t.set noreorder\n\t" ".set mips3\n\t" "cache 9, (%0)\n\t" ".set mips0\n\t" ".set reorder\n\t" : : "r" (begin)); __asm__ __volatile__("\n\t.set noreorder\n\t" ".set mips3\n\t" "cache 11, (%0)\n\t" ".set mips0\n\t" ".set reorder\n\t" : : "r" (begin)); /* Now search for the wrap around point. */ pow2 = (128 * 1024); tmp = 0; for(addr = (begin + (128 * 1024)); addr < (end); addr = (begin + pow2)) { __asm__ __volatile__("\n\t.set noreorder\n\t" ".set mips3\n\t" "cache 7, (%0)\n\t" ".set mips0\n\t" ".set reorder\n\t" : : "r" (addr)); __asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */ if(!get_taglo()) break; pow2 <<= 1; } __restore_flags(flags); addr -= begin; printk("Secondary cache sized at %dK linesize %d bytes.\n", (int) (addr >> 10), sc_lsize); scache_size = addr; return 1; } static void __init setup_noscache_funcs(void) { _clear_page = (void *)mips32_clear_page_dc; _copy_page = (void *)mips32_copy_page_dc; _flush_cache_all = mips32_flush_cache_all_pc; ___flush_cache_all = mips32_flush_cache_all_pc; _flush_cache_mm = mips32_flush_cache_mm_pc; _flush_cache_range = mips32_flush_cache_range_pc; _flush_cache_page = mips32_flush_cache_page_pc; _flush_page_to_ram = mips32_flush_page_to_ram_pc; _flush_icache_page = mips32_flush_icache_page; _dma_cache_wback_inv = mips32_dma_cache_wback_inv_pc; _dma_cache_wback = mips32_dma_cache_wback; _dma_cache_inv = mips32_dma_cache_inv_pc; } static void __init setup_scache_funcs(void) { _flush_cache_all = mips32_flush_cache_all_sc; ___flush_cache_all = mips32_flush_cache_all_sc; _flush_cache_mm = mips32_flush_cache_mm_sc; _flush_cache_range = mips32_flush_cache_range_sc; _flush_cache_page = mips32_flush_cache_page_sc; _flush_page_to_ram = mips32_flush_page_to_ram_sc; _clear_page = (void *)mips32_clear_page_sc; _copy_page = (void *)mips32_copy_page_sc; _flush_icache_page = mips32_flush_icache_page_s; _dma_cache_wback_inv = mips32_dma_cache_wback_inv_sc; _dma_cache_wback = mips32_dma_cache_wback; _dma_cache_inv = mips32_dma_cache_inv_sc; } typedef int (*probe_func_t)(unsigned long); static inline void __init setup_scache(unsigned int config) { probe_func_t probe_scache_kseg1; int sc_present = 0; /* Maybe the cpu knows about a l2 cache? */ probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); sc_present = probe_scache_kseg1(config); if (sc_present) { mips_cpu.scache.linesz = sc_lsize; /* * We cannot infer associativity - assume direct map * unless probe template indicates otherwise */ if(!mips_cpu.scache.ways) mips_cpu.scache.ways = 1; mips_cpu.scache.sets = (scache_size / sc_lsize) / mips_cpu.scache.ways; setup_scache_funcs(); return; } setup_noscache_funcs(); } static void __init probe_tlb(unsigned long config) { unsigned long config1; if (!(config & (1 << 31))) { /* * Not a MIPS32 complainant CPU. * Config 1 register not supported, we assume R4k style. */ mips_cpu.tlbsize = 48; } else { config1 = read_mips32_cp0_config1(); if (!((config >> 7) & 3)) panic("No MMU present"); else mips_cpu.tlbsize = ((config1 >> 25) & 0x3f) + 1; } printk("Number of TLB entries %d.\n", mips_cpu.tlbsize); } void __init ld_mmu_mips32(void) { unsigned long config = read_32bit_cp0_register(CP0_CONFIG); printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); #ifdef CONFIG_MIPS_UNCACHED change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); #else change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); #endif probe_icache(config); probe_dcache(config); setup_scache(config); probe_tlb(config); _flush_cache_sigtramp = mips32_flush_cache_sigtramp; _flush_icache_range = mips32_flush_icache_range; /* Ouch */ __flush_cache_all(); } --- NEW FILE: c-r3k.c --- /* * r2300.c: R2000 and R3000 specific mmu/cache code. * * Copyright (C) 1996 David S. Miller (dm...@en...) * * with a lot of changes to make this thing work for R3000s * Tx39XX R4k style caches added. HK * Copyright (C) 1998, 1999, 2000 Harald Koerfgen * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> #include <asm/system.h> #include <asm/isadep.h> #include <asm/io.h> #include <asm/wbflush.h> #include <asm/bootinfo.h> #include <asm/cpu.h> static unsigned long icache_size, dcache_size; /* Size in bytes */ static unsigned long icache_lsize, dcache_lsize; /* Size in bytes */ #undef DEBUG_CACHE unsigned long __init r3k_cache_size(unsigned long ca_flags) { unsigned long flags, status, dummy, size; volatile unsigned long *p; p = (volatile unsigned long *) KSEG0; flags = read_32bit_cp0_register(CP0_STATUS); /* isolate cache space */ write_32bit_cp0_register(CP0_STATUS, (ca_flags|flags)&~ST0_IEC); *p = 0xa5a55a5a; dummy = *p; status = read_32bit_cp0_register(CP0_STATUS); if (dummy != 0xa5a55a5a || (status & ST0_CM)) { size = 0; } else { for (size = 128; size <= 0x40000; size <<= 1) *(p + size) = 0; *p = -1; for (size = 128; (size <= 0x40000) && (*(p + size) == 0); size <<= 1) ; if (size > 0x40000) size = 0; } write_32bit_cp0_register(CP0_STATUS, flags); return size * sizeof(*p); } unsigned long __init r3k_cache_lsize(unsigned long ca_flags) { unsigned long flags, status, lsize, i; volatile unsigned long *p; p = (volatile unsigned long *) KSEG0; flags = read_32bit_cp0_register(CP0_STATUS); /* isolate cache space */ write_32bit_cp0_register(CP0_STATUS, (ca_flags|flags)&~ST0_IEC); for (i = 0; i < 128; i++) *(p + i) = 0; *(volatile unsigned char *)p = 0; for (lsize = 1; lsize < 128; lsize <<= 1) { *(p + lsize); status = read_32bit_cp0_register(CP0_STATUS); if (!(status & ST0_CM)) break; } for (i = 0; i < 128; i += lsize) *(volatile unsigned char *)(p + i) = 0; write_32bit_cp0_register(CP0_STATUS, flags); return lsize * sizeof(*p); } static void __init r3k_probe_cache(void) { dcache_size = r3k_cache_size(ST0_ISC); if (dcache_size) dcache_lsize = r3k_cache_lsize(ST0_ISC); icache_size = r3k_cache_size(ST0_ISC|ST0_SWC); if (icache_size) icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC); } static void r3k_flush_icache_range(unsigned long start, unsigned long end) { unsigned long size, i, flags; volatile unsigned char *p = (char *)start; size = end - start; if (size > icache_size) size = icache_size; flags = read_32bit_cp0_register(CP0_STATUS); /* isolate cache space */ write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|ST0_SWC|flags)&~ST0_IEC); for (i = 0; i < size; i += 0x080) { asm ( "sb\t$0, 0x000(%0)\n\t" "sb\t$0, 0x004(%0)\n\t" "sb\t$0, 0x008(%0)\n\t" "sb\t$0, 0x00c(%0)\n\t" "sb\t$0, 0x010(%0)\n\t" "sb\t$0, 0x014(%0)\n\t" "sb\t$0, 0x018(%0)\n\t" "sb\t$0, 0x01c(%0)\n\t" "sb\t$0, 0x020(%0)\n\t" "sb\t$0, 0x024(%0)\n\t" "sb\t$0, 0x028(%0)\n\t" "sb\t$0, 0x02c(%0)\n\t" "sb\t$0, 0x030(%0)\n\t" "sb\t$0, 0x034(%0)\n\t" "sb\t$0, 0x038(%0)\n\t" "sb\t$0, 0x03c(%0)\n\t" "sb\t$0, 0x040(%0)\n\t" "sb\t$0, 0x044(%0)\n\t" "sb\t$0, 0x048(%0)\n\t" "sb\t$0, 0x04c(%0)\n\t" "sb\t$0, 0x050(%0)\n\t" "sb\t$0, 0x054(%0)\n\t" "sb\t$0, 0x058(%0)\n\t" "sb\t$0, 0x05c(%0)\n\t" "sb\t$0, 0x060(%0)\n\t" "sb\t$0, 0x064(%0)\n\t" "sb\t$0, 0x068(%0)\n\t" "sb\t$0, 0x06c(%0)\n\t" "sb\t$0, 0x070(%0)\n\t" "sb\t$0, 0x074(%0)\n\t" "sb\t$0, 0x078(%0)\n\t" "sb\t$0, 0x07c(%0)\n\t" : : "r" (p) ); p += 0x080; } write_32bit_cp0_register(CP0_STATUS, flags); } static void r3k_flush_dcache_range(unsigned long start, unsigned long end) { unsigned long size, i, flags; volatile unsigned char *p = (char *)start; size = end - start; if (size > dcache_size) size = dcache_size; flags = read_32bit_cp0_register(CP0_STATUS); /* isolate cache space */ write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|flags)&~ST0_IEC); for (i = 0; i < size; i += 0x080) { asm ( "sb\t$0, 0x000(%0)\n\t" "sb\t$0, 0x004(%0)\n\t" "sb\t$0, 0x008(%0)\n\t" "sb\t$0, 0x00c(%0)\n\t" "sb\t$0, 0x010(%0)\n\t" "sb\t$0, 0x014(%0)\n\t" "sb\t$0, 0x018(%0)\n\t" "sb\t$0, 0x01c(%0)\n\t" "sb\t$0, 0x020(%0)\n\t" "sb\t$0, 0x024(%0)\n\t" "sb\t$0, 0x028(%0)\n\t" "sb\t$0, 0x02c(%0)\n\t" "sb\t$0, 0x030(%0)\n\t" "sb\t$0, 0x034(%0)\n\t" "sb\t$0, 0x038(%0)\n\t" "sb\t$0, 0x03c(%0)\n\t" "sb\t$0, 0x040(%0)\n\t" "sb\t$0, 0x044(%0)\n\t" "sb\t$0, 0x048(%0)\n\t" "sb\t$0, 0x04c(%0)\n\t" "sb\t$0, 0x050(%0)\n\t" "sb\t$0, 0x054(%0)\n\t" "sb\t$0, 0x058(%0)\n\t" "sb\t$0, 0x05c(%0)\n\t" "sb\t$0, 0x060(%0)\n\t" "sb\t$0, 0x064(%0)\n\t" "sb\t$0, 0x068(%0)\n\t" "sb\t$0, 0x06c(%0)\n\t" "sb\t$0, 0x070(%0)\n\t" "sb\t$0, 0x074(%0)\n\t" "sb\t$0, 0x078(%0)\n\t" "sb\t$0, 0x07c(%0)\n\t" : : "r" (p) ); p += 0x080; } write_32bit_cp0_register(CP0_STATUS,flags); } static inline unsigned long get_phys_page (unsigned long addr, struct mm_struct *mm) { pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long physpage; pgd = pgd_offset(mm, addr); pmd = pmd_offset(pgd, addr); pte = pte_offset(pmd, addr); if ((physpage = pte_val(*pte)) & _PAGE_VALID) return KSEG0ADDR(physpage & PAGE_MASK); return 0; } static inline void r3k_flush_cache_all(void) { r3k_flush_icache_range(KSEG0, KSEG0 + icache_size); } static void r3k_flush_cache_mm(struct mm_struct *mm) { if (mm->context != 0) { #ifdef DEBUG_CACHE printk("cmm[%d]", (int)mm->context); #endif r3k_flush_cache_all(); } } static void r3k_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { struct vm_area_struct *vma; if (mm->context == 0) return; start &= PAGE_MASK; #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif vma = find_vma(mm, start); if (!vma) return; if (mm->context != current->active_mm->context) { flush_cache_all(); } else { unsigned long flags, physpage; save_and_cli(flags); while (start < end) { if ((physpage = get_phys_page(start, mm))) r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); start += PAGE_SIZE; } restore_flags(flags); } } static void r3k_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; if (mm->context == 0) return; #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif if (vma->vm_flags & VM_EXEC) { unsigned long physpage; if ((physpage = get_phys_page(page, vma->vm_mm))) r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); } } static void r3k_flush_page_to_ram(struct page * page) { /* * Nothing to be done */ } static void r3k_flush_icache_page(struct vm_area_struct *vma, struct page *page) { struct mm_struct *mm = vma->vm_mm; unsigned long physpage; if (mm->context == 0) return; if (!(vma->vm_flags & VM_EXEC)) return; #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif physpage = (unsigned long) page_address(page); if (physpage) r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); } static void r3k_flush_cache_sigtramp(unsigned long addr) { unsigned long flags; #ifdef DEBUG_CACHE printk("csigtramp[%08lx]", addr); #endif flags = read_32bit_cp0_register(CP0_STATUS); write_32bit_cp0_register(CP0_STATUS, flags&~ST0_IEC); /* Fill the TLB to avoid an exception with caches isolated. */ asm ( "lw\t$0, 0x000(%0)\n\t" "lw\t$0, 0x004(%0)\n\t" : : "r" (addr) ); write_32bit_cp0_register(CP0_STATUS, (ST0_ISC|ST0_SWC|flags)&~ST0_IEC); asm ( "sb\t$0, 0x000(%0)\n\t" "sb\t$0, 0x004(%0)\n\t" : : "r" (addr) ); write_32bit_cp0_register(CP0_STATUS, flags); } static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) { wbflush(); r3k_flush_dcache_range(start, start + size); } void __init ld_mmu_r23000(void) { unsigned long config; printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); _clear_page = r3k_clear_page; _copy_page = r3k_copy_page; r3k_probe_cache(); _flush_cache_all = r3k_flush_cache_all; ___flush_cache_all = r3k_flush_cache_all; _flush_cache_mm = r3k_flush_cache_mm; _flush_cache_range = r3k_flush_cache_range; _flush_cache_page = r3k_flush_cache_page; _flush_cache_sigtramp = r3k_flush_cache_sigtramp; _flush_page_to_ram = r3k_flush_page_to_ram; _flush_icache_page = r3k_flush_icache_page; _flush_icache_range = r3k_flush_icache_range; _dma_cache_wback_inv = r3k_dma_cache_wback_inv; printk("Primary instruction cache %dkb, linesize %d bytes\n", (int) (icache_size >> 10), (int) icache_lsize); printk("Primary data cache %dkb, linesize %d bytes\n", (int) (dcache_size >> 10), (int) dcache_lsize); } --- NEW FILE: c-r4k.c --- /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * r4xx0.c: R4000 processor variant specific MMU/Cache routines. * * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ra...@gn... * * To do: * * - this code is a overbloated pig * - many of the bug workarounds are not efficient at all, but at * least they are functional ... */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> [...1538 lines suppressed...] probe_icache(config); probe_dcache(config); setup_scache(config); switch(mips_cpu.cputype) { case CPU_R4600: /* QED style two way caches? */ case CPU_R4700: case CPU_R5000: case CPU_NEVADA: _flush_cache_page = r4k_flush_cache_page_d32i32_r4600; } _flush_cache_sigtramp = r4k_flush_cache_sigtramp; _flush_icache_range = r4k_flush_icache_range; /* Ouch */ if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2020) { _flush_cache_sigtramp = r4600v20k_flush_cache_sigtramp; } __flush_cache_all(); } --- NEW FILE: c-r5432.c --- /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * r5432.c: NEC Vr5432 processor. We cannot use r4xx0.c because of * its unique way-selection method for indexed operations. * * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle (ra...@gn...) * Copyright (C) 2000 Jun Sun (js...@mv...) * */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/bcache.h> #include <asm/io.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/bootinfo.h> #include <asm/mmu_context.h> /* CP0 hazard avoidance. */ #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ "nop; nop; nop; nop; nop; nop;\n\t" \ ".set reorder\n\t") #include <asm/asm.h> #include <asm/cacheops.h> #undef DEBUG_CACHE /* Primary cache parameters. */ static int icache_size, dcache_size; /* Size in bytes */ static int ic_lsize, dc_lsize; /* LineSize in bytes */ /* -------------------------------------------------------------------- */ /* #include <asm/r4kcache.h> */ extern inline void flush_icache_line_indexed(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" "cache %1, 1(%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (addr), "i" (Index_Invalidate_I)); } extern inline void flush_dcache_line_indexed(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" "cache %1, 1(%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (addr), "i" (Index_Writeback_Inv_D)); } extern inline void flush_icache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (addr), "i" (Hit_Invalidate_I)); } extern inline void flush_dcache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (addr), "i" (Hit_Writeback_Inv_D)); } extern inline void invalidate_dcache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (addr), "i" (Hit_Invalidate_D)); } /* * The next two are for badland addresses like signal trampolines. */ extern inline void protected_flush_icache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n" "1:\tcache %1,(%0)\n" "2:\t.set mips0\n\t" ".set reorder\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,2b\n\t" ".previous" : : "r" (addr), "i" (Hit_Invalidate_I)); } extern inline void protected_writeback_dcache_line(unsigned long addr) { __asm__ __volatile__( ".set noreorder\n\t" ".set mips3\n" "1:\tcache %1,(%0)\n" "2:\t.set mips0\n\t" ".set reorder\n\t" ".section\t__ex_table,\"a\"\n\t" STR(PTR)"\t1b,2b\n\t" ".previous" : : "r" (addr), "i" (Hit_Writeback_D)); } #define cache32_unroll32(base,op) \ __asm__ __volatile__(" \ .set noreorder; \ .set mips3; \ cache %1, 0x000(%0); cache %1, 0x020(%0); \ cache %1, 0x040(%0); cache %1, 0x060(%0); \ cache %1, 0x080(%0); cache %1, 0x0a0(%0); \ cache %1, 0x0c0(%0); cache %1, 0x0e0(%0); \ cache %1, 0x100(%0); cache %1, 0x120(%0); \ cache %1, 0x140(%0); cache %1, 0x160(%0); \ cache %1, 0x180(%0); cache %1, 0x1a0(%0); \ cache %1, 0x1c0(%0); cache %1, 0x1e0(%0); \ cache %1, 0x200(%0); cache %1, 0x220(%0); \ cache %1, 0x240(%0); cache %1, 0x260(%0); \ cache %1, 0x280(%0); cache %1, 0x2a0(%0); \ cache %1, 0x2c0(%0); cache %1, 0x2e0(%0); \ cache %1, 0x300(%0); cache %1, 0x320(%0); \ cache %1, 0x340(%0); cache %1, 0x360(%0); \ cache %1, 0x380(%0); cache %1, 0x3a0(%0); \ cache %1, 0x3c0(%0); cache %1, 0x3e0(%0); \ .set mips0; \ .set reorder" \ : \ : "r" (base), \ "i" (op)); extern inline void blast_dcache32(void) { unsigned long start = KSEG0; unsigned long end = (start + dcache_size/2); while(start < end) { cache32_unroll32(start,Index_Writeback_Inv_D); cache32_unroll32(start+1,Index_Writeback_Inv_D); start += 0x400; } } extern inline void blast_dcache32_page(unsigned long page) { unsigned long start = page; unsigned long end = (start + PAGE_SIZE); while(start < end) { cache32_unroll32(start,Hit_Writeback_Inv_D); start += 0x400; } } extern inline void blast_dcache32_page_indexed(unsigned long page) { unsigned long start = page; unsigned long end = (start + PAGE_SIZE); while(start < end) { cache32_unroll32(start,Index_Writeback_Inv_D); cache32_unroll32(start+1,Index_Writeback_Inv_D); start += 0x400; } } extern inline void blast_icache32(void) { unsigned long start = KSEG0; unsigned long end = (start + icache_size/2); while(start < end) { cache32_unroll32(start,Index_Invalidate_I); cache32_unroll32(start+1,Index_Invalidate_I); start += 0x400; } } extern inline void blast_icache32_page(unsigned long page) { unsigned long start = page; unsigned long end = (start + PAGE_SIZE); while(start < end) { cache32_unroll32(start,Hit_Invalidate_I); start += 0x400; } } extern inline void blast_icache32_page_indexed(unsigned long page) { unsigned long start = page; unsigned long end = (start + PAGE_SIZE); while(start < end) { cache32_unroll32(start,Index_Invalidate_I); cache32_unroll32(start+1,Index_Invalidate_I); start += 0x400; } } /* -------------------------------------------------------------------- */ /* * If you think for one second that this stuff coming up is a lot * of bulky code eating too many kernel cache lines. Think _again_. * * Consider: * 1) Taken branches have a 3 cycle penalty on R4k * 2) The branch itself is a real dead cycle on even R4600/R5000. * 3) Only one of the following variants of each type is even used by * the kernel based upon the cache parameters we detect at boot time. * * QED. */ static inline void r5432_flush_cache_all_d32i32(void) { blast_dcache32(); blast_icache32(); } static void r5432_flush_cache_range_d32i32(struct mm_struct *mm, unsigned long start, unsigned long end) { if (mm->context != 0) { #ifdef DEBUG_CACHE printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end); #endif blast_dcache32(); blast_icache32(); } } /* * On architectures like the Sparc, we could get rid of lines in * the cache created only by a certain context, but on the MIPS * (and actually certain Sparc's) we cannot. */ static void r5432_flush_cache_mm_d32i32(struct mm_struct *mm) { if (mm->context != 0) { #ifdef DEBUG_CACHE printk("cmm[%d]", (int)mm->context); #endif r5432_flush_cache_all_d32i32(); } } static void r5432_flush_cache_page_d32i32(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; /* * If ownes no valid ASID yet, cannot possibly have gotten * this page into the cache. */ if (mm->context == 0) return; #ifdef DEBUG_CACHE printk("cpage[%d,%08lx]", (int)mm->context, page); #endif page &= PAGE_MASK; pgdp = pgd_offset(mm, page); pmdp = pmd_offset(pgdp, page); ptep = pte_offset(pmdp, page); /* * If the page isn't marked valid, the page cannot possibly be * in the cache. */ if (!(pte_val(*ptep) & _PAGE_PRESENT)) return; /* * Doing flushes for another ASID than the current one is * too difficult since stupid R4k caches do a TLB translation * for every cache flush operation. So we do indexed flushes * in that case, which doesn't overly flush the cache too much. */ if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { blast_dcache32_page(page); } else { /* * Do indexed flush, too much work to get the (possible) * tlb refills to work correctly. */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache32_page_indexed(page); } } /* If the addresses passed to these routines are valid, they are * either: * * 1) In KSEG0, so we can do a direct flush of the page. * 2) In KSEG2, and since every process can translate those * addresses all the time in kernel mode we can do a direct * flush. * 3) In KSEG1, no flush necessary. */ static void r5432_flush_page_to_ram_d32(struct page *page) { blast_dcache32_page((unsigned long)page_address(page)); } static void r5432_flush_icache_range(unsigned long start, unsigned long end) { r5432_flush_cache_all_d32i32(); } /* * Ok, this seriously sucks. We use them to flush a user page but don't * know the virtual address, so we have to blast away the whole icache * which is significantly more expensive than the real thing. */ static void r5432_flush_icache_page_i32(struct vm_area_struct *vma, struct page *page) { if (!(vma->vm_flags & VM_EXEC)) return; r5432_flush_cache_all_d32i32(); } /* * Writeback and invalidate the primary cache dcache before DMA. */ static void r5432_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size) { unsigned long end, a; if (size >= dcache_size) { flush_cache_all(); } else { a = addr & ~(dc_lsize - 1); end = (addr + size) & ~(dc_lsize - 1); while (1) { flush_dcache_line(a); /* Hit_Writeback_Inv_D */ if (a == end) break; a += dc_lsize; } } bc_wback_inv(addr, size); } static void r5432_dma_cache_inv_pc(unsigned long addr, unsigned long size) { unsigned long end, a; if (size >= dcache_size) { flush_cache_all(); } else { a = addr & ~(dc_lsize - 1); end = (addr + size) & ~(dc_lsize - 1); while (1) { flush_dcache_line(a); /* Hit_Writeback_Inv_D */ if (a == end) break; a += dc_lsize; } } bc_inv(addr, size); } static void r5432_dma_cache_wback(unsigned long addr, unsigned long size) { panic("r5432_dma_cache called - should not happen.\n"); } /* * While we're protected against bad userland addresses we don't care * very much about what happens in that case. Usually a segmentation * fault will dump the process later on anyway ... */ static void r5432_flush_cache_sigtramp(unsigned long addr) { protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); protected_flush_icache_line(addr & ~(ic_lsize - 1)); } /* Detect and size the various r4k caches. */ static void __init probe_icache(unsigned long config) { icache_size = 1 << (12 + ((config >> 9) & 7)); ic_lsize = 16 << ((config >> 5) & 1); printk("Primary instruction cache %dkb, linesize %d bytes.\n", icache_size >> 10, ic_lsize); } static void __init probe_dcache(unsigned long config) { dcache_size = 1 << (12 + ((config >> 6) & 7)); dc_lsize = 16 << ((config >> 4) & 1); printk("Primary data cache %dkb, linesize %d bytes.\n", dcache_size >> 10, dc_lsize); } void __init ld_mmu_r5432(void) { unsigned long config = read_32bit_cp0_register(CP0_CONFIG); printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); probe_icache(config); probe_dcache(config); _clear_page = r5432_clear_page_d32; _copy_page = r5432_copy_page_d32; _flush_cache_all = r5432_flush_cache_all_d32i32; ___flush_cache_all = r5432_flush_cache_all_d32i32; _flush_page_to_ram = r5432_flush_page_to_ram_d32; _flush_cache_mm = r5432_flush_cache_mm_d32i32; _flush_cache_range = r5432_flush_cache_range_d32i32; _flush_cache_page = r5432_flush_cache_page_d32i32; _flush_icache_page = r5432_flush_icache_page_i32; _dma_cache_wback_inv = r5432_dma_cache_wback_inv_pc; _dma_cache_wback = r5432_dma_cache_wback; _dma_cache_inv = r5432_dma_cache_inv_pc; _flush_cache_sigtramp = r5432_flush_cache_sigtramp; _flush_icache_range = r5432_flush_icache_range; /* Ouch */ __flush_cache_all(); } --- NEW FILE: c-rm7k.c --- /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * r4xx0.c: R4000 processor variant specific MMU/Cache routines. * * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 1998 Ralf Baechle ra...@gn... * * To do: * * - this code is a overbloated pig * - many of the bug workarounds are not efficient at all, but at * least they are functional ... */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <asm/io.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/bootinfo.h> #include <asm/mmu_context.h> /* CP0 hazard avoidance. */ #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ "nop; nop; nop; nop; nop; nop;\n\t" \ ".set reorder\n\t") /* Primary cache parameters. */ static int icache_size, dcache_size; /* Size in bytes */ #define ic_lsize 32 /* Fixed to 32 byte on RM7000 */ #define dc_lsize 32 /* Fixed to 32 byte on RM7000 */ #define sc_lsize 32 /* Fixed to 32 byte on RM7000 */ #define tc_pagesize (32*128) /* Secondary cache parameters. */ #define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ #include <asm/cacheops.h> #include <asm/r4kcache.h> int rm7k_tcache_enabled = 0; /* * Not added to asm/r4kcache.h because it seems to be RM7000-specific. */ #define Page_Invalidate_T 0x16 static inline void invalidate_tcache_page(unsigned long addr) { __asm__ __volatile__( ".set\tnoreorder\t\t\t# invalidate_tcache_page\n\t" ".set\tmips3\n\t" "cache\t%1, (%0)\n\t" ".set\tmips0\n\t" ".set\treorder" : : "r" (addr), "i" (Page_Invalidate_T)); } static void __flush_cache_all_d32i32(void) { blast_dcache32(); blast_icache32(); } static inline void rm7k_flush_cache_all_d32i32(void) { /* Yes! Caches that don't suck ... */ } static void rm7k_flush_cache_range_d32i32(struct mm_struct *mm, unsigned long start, unsigned long end) { /* RM7000 caches are sane ... */ } static void rm7k_flush_cache_mm_d32i32(struct mm_struct *mm) { /* RM7000 caches are sane ... */ } static void rm7k_flush_cache_page_d32i32(struct vm_area_struct *vma, unsigned long page) { /* RM7000 caches are sane ... */ } static void rm7k_flush_page_to_ram_d32i32(struct page * page) { /* Yes! Caches that don't suck! */ } static void rm7k_flush_icache_range(unsigned long start, unsigned long end) { /* * FIXME: This is overdoing things and harms performance. */ __flush_cache_all_d32i32(); } static void rm7k_flush_icache_page(struct vm_area_struct *vma, struct page *page) { /* * FIXME: We should not flush the entire cache but establish some * temporary mapping and use hit_invalidate operation to flush out * the line from the cache. */ __flush_cache_all_d32i32(); } /* * Writeback and invalidate the primary cache dcache before DMA. * (XXX These need to be fixed ...) */ static void rm7k_dma_cache_wback_inv(unsigned long addr, unsigned long size) { unsigned long end, a; a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { flush_dcache_line(a); /* Hit_Writeback_Inv_D */ flush_icache_line(a); /* Hit_Invalidate_I */ flush_scache_line(a); /* Hit_Writeback_Inv_SD */ if (a == end) break; a += sc_lsize; } if (!rm7k_tcache_enabled) return; a = addr & ~(tc_pagesize - 1); end = (addr + size) & ~(tc_pagesize - 1); while(1) { invalidate_tcache_page(a); /* Page_Invalidate_T */ if (a == end) break; a += tc_pagesize; } } static void rm7k_dma_cache_inv(unsigned long addr, unsigned long size) { unsigned long end, a; a = addr & ~(sc_lsize - 1); end = (addr + size) & ~(sc_lsize - 1); while (1) { invalidate_dcache_line(a); /* Hit_Invalidate_D */ flush_icache_line(a); /* Hit_Invalidate_I */ invalidate_scache_line(a); /* Hit_Invalidate_SD */ if (a == end) break; a += sc_lsize; } if (!rm7k_tcache_enabled) return; a = addr & ~(tc_pagesize - 1); end = (addr + size) & ~(tc_pagesize - 1); while(1) { invalidate_tcache_page(a); /* Page_Invalidate_T */ if (a == end) break; a += tc_pagesize; } } static void rm7k_dma_cache_wback(unsigned long addr, unsigned long size) { panic("rm7k_dma_cache_wback called - should not happen.\n"); } /* * While we're protected against bad userland addresses we don't care * very much about what happens in that case. Usually a segmentation * fault will dump the process later on anyway ... */ static void rm7k_flush_cache_sigtramp(unsigned long addr) { protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); protected_flush_icache_line(addr & ~(ic_lsize - 1)); } /* Detect and size the caches. */ static inline void probe_icache(unsigned long config) { icache_size = 1 << (12 + ((config >> 9) & 7)); printk(KERN_INFO "Primary instruction cache %dKiB.\n", icache_size >> 10); } static inline void probe_dcache(unsigned long config) { dcache_size = 1 << (12 + ((config >> 6) & 7)); printk(KERN_INFO "Primary data cache %dKiB.\n", dcache_size >> 10); } /* * This function is executed in the uncached segment KSEG1. * It must not touch the stack, because the stack pointer still points * into KSEG0. * * Three options: * - Write it in assembly and guarantee that we don't use the stack. * - Disable caching for KSEG0 before calling it. * - Pray that GCC doesn't randomly start using the stack. * * This being Linux, we obviously take the least sane of those options - * following DaveM's lead in r4xx0.c * * It seems we get our kicks from relying on unguaranteed behaviour in GCC */ static __init void setup_scache(void) { int register i; set_cp0_config(1<<3 /* CONF_SE */); set_taglo(0); set_taghi(0); for (i=0; i<scache_size; i+=sc_lsize) { __asm__ __volatile__ ( ".set noreorder\n\t" ".set mips3\n\t" "cache %1, (%0)\n\t" ".set mips0\n\t" ".set reorder" : : "r" (KSEG0ADDR(i)), "i" (Index_Store_Tag_SD)); } } static inline void probe_scache(unsigned long config) { void (*func)(void) = KSEG1ADDR(&setup_scache); if ((config >> 31) & 1) return; printk(KERN_INFO "Secondary cache %dKiB, linesize %d bytes.\n", (scache_size >> 10), sc_lsize); if ((config >> 3) & 1) return; printk(KERN_INFO "Enabling secondary cache..."); func(); printk("Done\n"); } static inline void probe_tcache(unsigned long config) { if ((config >> 17) & 1) return; /* We can't enable the L3 cache yet. There may be board-specific * magic necessary to turn it on, and blindly asking the CPU to * start using it would may give cache errors. * * Also, board-specific knowledge may allow us to use the * CACHE Flash_Invalidate_T instruction if the tag RAM supports * it, and may specify the size of the L3 cache so we don't have * to probe it. */ printk(KERN_INFO "Tertiary cache present, %s enabled\n", config&(1<<12) ? "already" : "not (yet)"); if ((config >> 12) & 1) rm7k_tcache_enabled = 1; } void __init ld_mmu_rm7k(void) { unsigned long config = read_32bit_cp0_register(CP0_CONFIG); unsigned long addr; printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); change_cp0_config(CONF_CM_CMASK, CONF_CM_UNCACHED); /* RM7000 erratum #31. The icache is screwed at startup. */ set_taglo(0); set_taghi(0); for (addr = KSEG0; addr <= KSEG0 + 4096; addr += ic_lsize) { __asm__ __volatile__ ( ".set noreorder\n\t" ".set mips3\n\t" "cache\t%1, 0(%0)\n\t" "cache\t%1, 0x1000(%0)\n\t" "cache\t%1, 0x2000(%0)\n\t" "cache\t%1, 0x3000(%0)\n\t" "cache\t%2, 0(%0)\n\t" "cache\t%2, 0x1000(%0)\n\t" "cache\t%2, 0x2000(%0)\n\t" "cache\t%2, 0x3000(%0)\n\t" "cache\t%1, 0(%0)\n\t" "cache\t%1, 0x1000(%0)\n\t" "cache\t%1, 0x2000(%0)\n\t" "cache\t%1, 0x3000(%0)\n\t" ".set\tmips0\n\t" ".set\treorder\n\t" : : "r" (addr), "i" (Index_Store_Tag_I), "i" (Fill)); } #ifndef CONFIG_MIPS_UNCACHED change_cp0_config(CONF_CM_CMASK, CONF_CM_CACHABLE_NONCOHERENT); #endif probe_icache(config); probe_dcache(config); probe_scache(config); probe_tcache(config); _clear_page = rm7k_clear_page; _copy_page = rm7k_copy_page; _flush_cache_all = rm7k_flush_cache_all_d32i32; ___flush_cache_all = __flush_cache_all_d32i32; _flush_cache_mm = rm7k_flush_cache_mm_d32i32; _flush_cache_range = rm7k_flush_cache_range_d32i32; _flush_cache_page = rm7k_flush_cache_page_d32i32; _flush_page_to_ram = rm7k_flush_page_to_ram_d32i32; _flush_cache_sigtramp = rm7k_flush_cache_sigtramp; _flush_icache_range = rm7k_flush_icache_range; _flush_icache_page = rm7k_flush_icache_page; _dma_cache_wback_inv = rm7k_dma_cache_wback_inv; _dma_cache_wback = rm7k_dma_cache_wback; _dma_cache_inv = rm7k_dma_cache_inv; __flush_cache_all_d32i32(); } --- NEW FILE: c-sb1.c --- /* * Copyright (C) 1996 David S. Miller (dm...@en...) * Copyright (C) 1997, 2001 Ralf Baechle (ra...@gn...) * Copyright (C) 2000, 2001 Broadcom Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * In this entire file, I'm not sure what the role of the L2 on the sb1250 * is. Since it is coherent to the system, we should never need to flush * it...right?...right??? -JDC */ #include <asm/mmu_context.h> /* These are probed at ld_mmu time */ static unsigned int icache_size; static unsigned int dcache_size; static unsigned int icache_line_size; static unsigned int dcache_line_size; static unsigned int icache_assoc; static unsigned int dcache_assoc; static unsigned int icache_sets; static unsigned int dcache_sets; static unsigned int tlb_entries; void local_flush_tlb_all(void) { unsigned long flags; unsigned long old_ctx; int entry; __save_and_cli(flags); /* Save old context and create impossible VPN2 value */ old_ctx = (get_entryhi() & 0xff); set_entrylo0(0); set_entrylo1(0); for (entry = 0; entry < tlb_entries; entry++) { set_entryhi(KSEG0 + (PAGE_SIZE << 1) * entry); set_index(entry); tlb_write_indexed(); } set_entryhi(old_ctx); __restore_flags(flags); } /* * The dcache is fully coherent to the system, with one * big caveat: the instruction stream. In other words, * if we miss in the icache, and have dirty data in the * L1 dcache, then we'll go out to memory (or the L2) and * get the not-as-recent data. * * So the only time we have to flush the dcache is when * we're flushing the icache. Since the L2 is fully * coherent to everything, including I/O, we never have * to flush it */ static void sb1_flush_cache_all(void) { /* * Haven't worried too much about speed here; given that we're flushing * the icache, the time to invalidate is dwarfed by the time it's going * to take to refill it. Register usage: * * $1 - moving cache index * $2 - set count */ if (icache_sets) { __asm__ __volatile__ ( ".set push \n" ".set noreorder \n" ".set noat \n" ".set mips4 \n" " move $1, %2 \n" /* Start at index 0 */ "1: cache 0, 0($1) \n" /* Invalidate this index */ " addiu %1, %1, -1 \n" /* Decrement loop count */ " bnez %1, 1b \n" /* loop test */ " addu $1, $1, %0 \n" /* Next address JDCXXX - Should be short piped */ ".set pop \n" ::"r" (icache_line_size), "r" (icache_sets * icache_assoc), "r" (KSEG0)); } if (dcache_sets) { __asm__ __volatile__ ( ".set push \n" ".set noreorder \n" ".set noat \n" ".set mips4 \n" " move $1, %2 \n" /* Start at index 0 */ "1: cache 0x1, 0($1) \n" /* WB/Invalidate this index */ " addiu %1, %1, -1 \n" /* Decrement loop count */ " bnez %1, 1b \n" /* loop test */ " addu $1, $1, %0 \n" /* Next address JDCXXX - Should be short piped */ ".set pop \n" : : "r" (dcache_line_size), "r" (dcache_sets * dcache_assoc), "r" (KSEG0)); } } /* * When flushing a range in the icache, we have to first writeback * the dcache for the same range, so new ifetches will see any * data that was dirty in the dcache */ static void sb1_flush_icache_range(unsigned long start, unsigned long end) { /* JDCXXX - Implement me! */ sb1_flush_cache_all(); } static void sb1_flush_cache_mm(struct mm_struct *mm) { /* Don't need to do this, as the dcache is physically tagged */ } static void sb1_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { /* Don't need to do this, as the dcache is physically tagged */ } static void sb1_flush_cache_sigtramp(unsigned long page) { /* JDCXXX - Implement me! */ sb1_flush_cache_all(); } /* * This only needs to make sure stores done up to this * point are visible to other agents outside the CPU. Given * the coherent nature of the ZBus, all that's required here is * a sync to make sure the data gets out to the caches and is * visible to an arbitrary A Phase from an external agent * * Actually, I'm not even sure that's necessary; the semantics * of this function aren't clear. If it's supposed to serve as * a memory barrier, this is needed. If it's only meant to * prevent data from being invisible to non-cpu memory accessors * for some indefinite period of time (e.g. in a non-coherent * dcache) then this function would be a complete nop. */ static void sb1_flush_page_to_ram(struct page *page) { __asm__ __volatile__( " sync \n" /* Short pipe */ :::"memory"); } /* Cribbed from the r2300 code */ static void sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { sb1_flush_cache_all(); #if 0 struct mm_struct *mm = vma->vm_mm; unsigned long physpage; /* No icache flush needed without context; */ if (mm->context == 0) return; /* No icache flush needed if the page isn't executable */ if (!(vma->vm_flags & VM_EXEC)) return; physpage = (unsigned long) page_address(page); if (physpage) sb1_flush_icache_range(physpage, physpage + PAGE_SIZE); #endif } /* * Cache set values (from the mips64 spec) * 0 - 64 * 1 - 128 * 2 - 256 * 3 - 512 * 4 - 1024 * 5 - 2048 * 6 - 4096 * 7 - Reserved */ static unsigned int decode_cache_sets(unsigned int config_field) { if (config_field == 7) { /* JDCXXX - Find a graceful way to abort. */ return 0; } return (1<<(config_field + 6)); } /* * Cache line size values (from the mips64 spec) * 0 - No cache present. * 1 - 4 bytes * 2 - 8 bytes * 3 - 16 bytes * 4 - 32 bytes * 5 - 64 bytes * 6 - 128 bytes * 7 - Reserved */ static unsigned int decode_cache_line_size(unsigned int config_field) { if (config_field == 0) { return 0; } else if (config_field == 7) { /* JDCXXX - Find a graceful way to abort. */ return 0; } return (1<<(config_field + 1)); } /* * Relevant bits of the config1 register format (from the MIPS32/MIPS64 specs) * * 24:22 Icache sets per way * 21:19 Icache line size * 18:16 Icache Associativity * 15:13 Dcache sets per way * 12:10 Dcache line size * 9:7 Dcache Associativity */ static void probe_cache_sizes(void) { u32 config1; __asm__ __volatile__( ".set push \n" ".set mips64 \n" " mfc0 %0, $16, 1 \n" /* Get config1 register */ ".set pop \n" :"=r" (config1)); icache_line_size = decode_cache_line_size((config1 >> 19) & 0x7); dcache_line_size = decode_cache_line_size((config1 >> 10) & 0x7); icache_sets = decode_cache_sets((config1 >> 22) & 0x7); dcache_sets = decode_cache_sets((config1 >> 13) & 0x7); icache_assoc = ((config1 >> 16) & 0x7) + 1; dcache_assoc = ((config1 >> 7) & 0x7) + 1; icache_size = icache_line_size * icache_sets * icache_assoc; dcache_size = dcache_line_size * dcache_sets * dcache_assoc; tlb_entries = ((config1 >> 25) & 0x3f) + 1; } /* This is called from loadmmu.c. We have to set up all the memory management function pointers, as well as initialize the caches and tlbs */ void ld_mmu_sb1(void) { probe_cache_sizes(); _clear_page = sb1_clear_page; _copy_page = sb1_copy_page; _flush_cache_all = sb1_flush_cache_all; _flush_cache_mm = sb1_flush_cache_mm; _flush_cache_range = sb1_flush_cache_range; _flush_cache_page = sb1_f... [truncated message content] |