patch-2.1.101 linux/arch/mips/mm/r4xx0.c

Next file: linux/arch/mips/mm/r6000.c
Previous file: linux/arch/mips/mm/r2300.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.100/linux/arch/mips/mm/r4xx0.c linux/arch/mips/mm/r4xx0.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
  *
- * $Id: r4xx0.c,v 1.11 1997/12/02 06:33:53 ralf Exp $
+ * $Id: r4xx0.c,v 1.11 1998/05/04 09:18:31 ralf Exp $
  *
  * To do:
  *
@@ -11,13 +11,13 @@
  *  - many of the bug workarounds are not efficient at all, but at
  *    least they are functional ...
  */
-#include <linux/config.h>
-
+#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/autoconf.h>
 
+#include <asm/bcache.h>
+#include <asm/io.h>
 #include <asm/sgi.h>
 #include <asm/sgimc.h>
 #include <asm/page.h>
@@ -45,6 +45,18 @@
 #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;
+
+/*
  * On processors with QED R4600 style two set assosicative cache
  * this is the bit which selects the way in the cache for the
  * indexed cachops.
@@ -53,9 +65,19 @@
 #define dcache_waybit (dcache_size >> 1)
 
 /*
- * Zero an entire page.  We have three flavours of the routine available.
- * One for CPU with 16byte, with 32byte cachelines plus a special version
- * with nops which handles the buggy R4600 v1.x.
+ * Zero an entire page.  Basically a simple unrolled loop should do the
+ * job but we want more performance by saving memory bus bandwidth.  We
+ * have five flavours of the routine available for:
+ *
+ * - 16byte cachelines and no second level cache
+ * - 32byte cachelines second level cache
+ * - a version which handles the buggy R4600 v1.x
+ * - a version which handles the buggy R4600 v2.0
+ * - Finally a last version without fancy cache games for the SC and MC
+ *   versions of R4000 and R4400.  Cache instructions are quite expensive
+ *   and I guess using them for both the primary and the second level cache
+ *   wouldn't be worth the effort.
+ *   This needs to be verified by benchmarking.
  */
 
 static void r4k_clear_page_d16(unsigned long page)
@@ -219,6 +241,32 @@
 	restore_flags(flags);
 }
 
+static void r4k_clear_page(unsigned long page)
+{
+	__asm__ __volatile__(
+		".set\tnoreorder\n\t"
+		".set\tnoat\n\t"
+		".set\tmips3\n\t"
+		"daddiu\t$1,%0,%2\n"
+		"1:\tsd\t$0,(%0)\n\t"
+		"sd\t$0,8(%0)\n\t"
+		"sd\t$0,16(%0)\n\t"
+		"sd\t$0,24(%0)\n\t"
+		"daddiu\t%0,64\n\t"
+		"sd\t$0,-32(%0)\n\t"
+		"sd\t$0,-24(%0)\n\t"
+		"sd\t$0,-16(%0)\n\t"
+		"bne\t$1,%0,1b\n\t"
+		"sd\t$0,-8(%0)\n\t"
+		".set\tmips0\n\t"
+		".set\tat\n\t"
+		".set\treorder"
+		:"=r" (page)
+		:"0" (page),
+		 "I" (PAGE_SIZE)
+		:"$1","memory");
+}
+
 
 /*
  * This is still inefficient.  We only can do better if we know the
@@ -477,6 +525,60 @@
 	restore_flags(flags);
 }
 
+static void r4k_copy_page(unsigned long to, unsigned long from)
+{
+	unsigned long dummy1, dummy2;
+	unsigned long reg1, reg2, reg3, reg4;
+
+	__asm__ __volatile__(
+		".set\tnoreorder\n\t"
+		".set\tnoat\n\t"
+		".set\tmips3\n\t"
+		"daddiu\t$1,%0,%8\n"
+		"1:\tlw\t%2,(%1)\n\t"
+		"lw\t%3,4(%1)\n\t"
+		"lw\t%4,8(%1)\n\t"
+		"lw\t%5,12(%1)\n\t"
+		"sw\t%2,(%0)\n\t"
+		"sw\t%3,4(%0)\n\t"
+		"sw\t%4,8(%0)\n\t"
+		"sw\t%5,12(%0)\n\t"
+		"lw\t%2,16(%1)\n\t"
+		"lw\t%3,20(%1)\n\t"
+		"lw\t%4,24(%1)\n\t"
+		"lw\t%5,28(%1)\n\t"
+		"sw\t%2,16(%0)\n\t"
+		"sw\t%3,20(%0)\n\t"
+		"sw\t%4,24(%0)\n\t"
+		"sw\t%5,28(%0)\n\t"
+		"daddiu\t%0,64\n\t"
+		"daddiu\t%1,64\n\t"
+		"lw\t%2,-32(%1)\n\t"
+		"lw\t%3,-28(%1)\n\t"
+		"lw\t%4,-24(%1)\n\t"
+		"lw\t%5,-20(%1)\n\t"
+		"sw\t%2,-32(%0)\n\t"
+		"sw\t%3,-28(%0)\n\t"
+		"sw\t%4,-24(%0)\n\t"
+		"sw\t%5,-20(%0)\n\t"
+		"lw\t%2,-16(%1)\n\t"
+		"lw\t%3,-12(%1)\n\t"
+		"lw\t%4,-8(%1)\n\t"
+		"lw\t%5,-4(%1)\n\t"
+		"sw\t%2,-16(%0)\n\t"
+		"sw\t%3,-12(%0)\n\t"
+		"sw\t%4,-8(%0)\n\t"
+		"bne\t$1,%0,1b\n\t"
+		"sw\t%5,-4(%0)\n\t"
+		".set\tmips0\n\t"
+		".set\tat\n\t"
+		".set\treorder"
+		:"=r" (dummy1), "=r" (dummy2),
+		 "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4)
+		:"0" (to), "1" (from),
+		 "I" (PAGE_SIZE));
+}
+
 /*
  * 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_.
@@ -1892,144 +1994,111 @@
  * in .pdf format.)
  */
 static void
-r4k_flush_cache_pre_dma_out_pc(unsigned long addr, unsigned long size)
+r4k_dma_cache_wback_inv_pc(unsigned long addr, unsigned long size)
 {
 	unsigned long end, a;
-	unsigned int cmode, flags;
-
-	cmode = read_32bit_cp0_register(CP0_CONFIG) & CONFIG_CM_CMASK;
-	if (cmode == CONFIG_CM_CACHABLE_WA ||
-	    cmode == CONFIG_CM_CACHABLE_NO_WA) {
-		/* primary dcache is writethrough, therefore memory
-		   is already consistent with the caches.  */
-		return;
-	}
+	unsigned int flags;
 
 	if (size >= dcache_size) {
 		flush_cache_all();
-		return;
-	}
-
-	/* Workaround for R4600 bug.  See comment above. */
-	save_and_cli(flags);
-	*(volatile unsigned long *)KSEG1;
+	} else {
+		/* Workaround for R4600 bug.  See comment above. */
+		save_and_cli(flags);
+		*(volatile unsigned long *)KSEG1;
 
-	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;
+		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);
 	}
-	restore_flags(flags);
+	bcops->bc_wback_inv(addr, size);
 }
 
 static void
-r4k_flush_cache_pre_dma_out_sc(unsigned long addr, unsigned long size)
+r4k_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size)
 {
-	unsigned long end;
-	unsigned long a;
+	unsigned long end, a;
+	unsigned int flags;
 
 	if (size >= scache_size) {
 		flush_cache_all();
-		return;
+	} 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);
 	}
 
 	a = addr & ~(sc_lsize - 1);
 	end = (addr + size) & ~(sc_lsize - 1);
 	while (1) {
-		flush_scache_line(addr); /* Hit_Writeback_Inv_SD */
-		if (addr == end) break;
-		addr += sc_lsize;
+		flush_scache_line(a);	/* Hit_Writeback_Inv_SD */
+		if (a == end) break;
+		a += sc_lsize;
 	}
-	r4k_flush_cache_pre_dma_out_pc(addr, size);
 }
 
 static void
-r4k_flush_cache_post_dma_in_pc(unsigned long addr, unsigned long size)
+r4k_dma_cache_inv_pc(unsigned long addr, unsigned long size)
 {
-	unsigned long end;
-	unsigned long a;
+	unsigned long end, a;
+	unsigned int flags;
 
 	if (size >= dcache_size) {
 		flush_cache_all();
-		return;
-	}
-
-	/* Workaround for R4600 bug.  See comment above. */
-	*(volatile unsigned long *)KSEG1;
+	} else {
+		/* Workaround for R4600 bug.  See comment above. */
+		save_and_cli(flags);
+		*(volatile unsigned long *)KSEG1;
 
-	a = addr & ~(dc_lsize - 1);
-	end = (addr + size) & ~(dc_lsize - 1);
-	while (1) {
-		invalidate_dcache_line(a); /* Hit_Invalidate_D */
-		if (a == end) break;
-		a += dc_lsize;
+		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);
 	}
+
+	bcops->bc_inv(addr, size);
 }
 
 static void
-r4k_flush_cache_post_dma_in_sc(unsigned long addr, unsigned long size)
+r4k_dma_cache_inv_sc(unsigned long addr, unsigned long size)
 {
-	unsigned long end;
-	unsigned long a;
+	unsigned long end, a;
+	unsigned int flags;
 
 	if (size >= scache_size) {
 		flush_cache_all();
-		return;
+	} 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);
 	}
 
 	a = addr & ~(sc_lsize - 1);
 	end = (addr + size) & ~(sc_lsize - 1);
 	while (1) {
-		invalidate_scache_line(addr); /* Hit_Invalidate_SD */
-		if (addr == end) break;
-		addr += sc_lsize;
-	}
-	r4k_flush_cache_pre_dma_out_pc(addr, size);
-}
-
-static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page)
-{
-	page &= PAGE_MASK;
-	if((page >= KSEG0 && page < KSEG1) || (page >= KSEG2)) {
-		unsigned long flags;
-
-#ifdef DEBUG_CACHE
-		printk("r4600_cram[%08lx]", page);
-#endif
-		save_and_cli(flags);
-		blast_dcache32_page(page);
-#ifdef CONFIG_SGI
-		{
-			unsigned long tmp1, tmp2;
-
-			__asm__ __volatile__("
-			.set noreorder
-			.set mips3
-			li	%0, 0x1
-			dsll	%0, 31
-			or	%0, %0, %2
-			lui	%1, 0x9000
-			dsll32	%1, 0
-			or	%0, %0, %1
-			daddu	%1, %0, 0x0fe0
-			li	%2, 0x80
-			mtc0	%2, $12
-			nop; nop; nop; nop;
-1:			sw	$0, 0(%0)
-			bltu	%0, %1, 1b
-			daddu	%0, 32
-			mtc0	$0, $12
-			nop; nop; nop; nop;
-			.set mips0
-			.set reorder"
-			: "=&r" (tmp1), "=&r" (tmp2),
-			  "=&r" (page)
-			: "2" (page & 0x0007f000));
-		}
-#endif /* CONFIG_SGI */
-		restore_flags(flags);
+		flush_scache_line(a); /* Hit_Writeback_Inv_SD */
+		if (a == end) break;
+		a += sc_lsize;
 	}
 }
 
@@ -2394,173 +2463,31 @@
 }
 
 /* Detect and size the various r4k caches. */
-static void probe_icache(unsigned long config)
+__initfunc(static void probe_icache(unsigned long config))
 {
-	unsigned long tmp;
-
-	tmp = (config >> 9) & 7;
-	icache_size = (1 << (12 + tmp));
-	if((config >> 5) & 1)
-		ic_lsize = 32;
-	else
-		ic_lsize = 16;
+	icache_size = 1 << (12 + ((config >> 6) & 7));
+	ic_lsize = 16 << ((config >> 4) & 1);
 
-	printk("Primary ICACHE %dK (linesize %d bytes)\n",
-	       (int)(icache_size >> 10), (int)ic_lsize);
+	printk("Primary instruction cache %dkb, linesize %d bytes)\n",
+	       icache_size >> 10, ic_lsize);
 }
 
-static void probe_dcache(unsigned long config)
+__initfunc(static void probe_dcache(unsigned long config))
 {
-	unsigned long tmp;
+	dcache_size = 1 << (12 + ((config >> 6) & 7));
+	dc_lsize = 16 << ((config >> 4) & 1);
 
-	tmp = (config >> 6) & 7;
-	dcache_size = (1 << (12 + tmp));
-	if((config >> 4) & 1)
-		dc_lsize = 32;
-	else
-		dc_lsize = 16;
-
-	printk("Primary DCACHE %dK (linesize %d bytes)\n",
-	       (int)(dcache_size >> 10), (int)dc_lsize);
+	printk("Primary data cache %dkb, linesize %d bytes)\n",
+	       dcache_size >> 10, dc_lsize);
 }
 
-static int probe_scache_eeprom(unsigned long config)
-{
-#ifdef CONFIG_SGI
-	volatile unsigned int *cpu_control;
-	unsigned short cmd = 0xc220;
-	unsigned long data = 0;
-	int i, n;
-
-#ifdef __MIPSEB__
-	cpu_control = (volatile unsigned int *) KSEG1ADDR(0x1fa00034);
-#else
-	cpu_control = (volatile unsigned int *) KSEG1ADDR(0x1fa00030);
-#endif
-#define DEASSERT(bit) (*(cpu_control) &= (~(bit)))
-#define ASSERT(bit) (*(cpu_control) |= (bit))
-#define DELAY  for(n = 0; n < 100000; n++) __asm__ __volatile__("")
-	DEASSERT(SGIMC_EEPROM_PRE);
-	DEASSERT(SGIMC_EEPROM_SDATAO);
-	DEASSERT(SGIMC_EEPROM_SECLOCK);
-	DEASSERT(SGIMC_EEPROM_PRE);
-	DELAY;
-	ASSERT(SGIMC_EEPROM_CSEL); ASSERT(SGIMC_EEPROM_SECLOCK);
-	for(i = 0; i < 11; i++) {
-		if(cmd & (1<<15))
-			ASSERT(SGIMC_EEPROM_SDATAO);
-		else
-			DEASSERT(SGIMC_EEPROM_SDATAO);
-		DEASSERT(SGIMC_EEPROM_SECLOCK);
-		ASSERT(SGIMC_EEPROM_SECLOCK);
-		cmd <<= 1;
-	}
-	DEASSERT(SGIMC_EEPROM_SDATAO);
-	for(i = 0; i < (sizeof(unsigned short) * 8); i++) {
-		unsigned int tmp;
-
-		DEASSERT(SGIMC_EEPROM_SECLOCK);
-		DELAY;
-		ASSERT(SGIMC_EEPROM_SECLOCK);
-		DELAY;
-		data <<= 1;
-		tmp = *cpu_control;
-		if(tmp & SGIMC_EEPROM_SDATAI)
-			data |= 1;
-	}
-	DEASSERT(SGIMC_EEPROM_SECLOCK);
-	DEASSERT(SGIMC_EEPROM_CSEL);
-	ASSERT(SGIMC_EEPROM_PRE);
-	ASSERT(SGIMC_EEPROM_SECLOCK);
-	data <<= PAGE_SHIFT;
-	printk("R4600/R5000 SCACHE size %dK ", (int) (data >> 10));
-	switch(mips_cputype) {
-	case CPU_R4600:
-	case CPU_R4640:
-		sc_lsize = 32;
-		break;
-
-	default:
-		sc_lsize = 128;
-		break;
-	}
-	printk("linesize %d bytes\n", sc_lsize);
-	scache_size = data;
-	if(data) {
-		unsigned long addr, tmp1, tmp2;
-
-		/* Enable r4600/r5000 cache.  But flush it first. */
-		for(addr = KSEG0; addr < (KSEG0 + dcache_size);
-		    addr += dc_lsize)
-			flush_dcache_line_indexed(addr);
-		for(addr = KSEG0; addr < (KSEG0 + icache_size);
-		    addr += ic_lsize)
-			flush_icache_line_indexed(addr);
-		for(addr = KSEG0; addr < (KSEG0 + scache_size);
-		    addr += sc_lsize)
-			flush_scache_line_indexed(addr);
-
-		/* R5000 scache enable is in CP0 config, on R4600 variants
-		 * the scache is enable by the memory mapped cache controller.
-		 */
-		if(mips_cputype == CPU_R5000) {
-			unsigned long config;
-
-			config = read_32bit_cp0_register(CP0_CONFIG);
-			config |= 0x1000;
-			write_32bit_cp0_register(CP0_CONFIG, config);
-		} else {
-			/* This is really cool... */
-			printk("Enabling R4600 SCACHE\n");
-			__asm__ __volatile__("
-			.set noreorder
-			.set mips3
-			mfc0	%2, $12
-			nop; nop; nop; nop;
-			li	%1, 0x80
-			mtc0	%1, $12
-			nop; nop; nop; nop;
-			li	%0, 0x1
-			dsll	%0, 31
-			lui	%1, 0x9000
-			dsll32	%1, 0
-			or	%0, %1, %0
-			sb	$0, 0(%0)
-			mtc0	$0, $12
-			nop; nop; nop; nop;
-			mtc0	%2, $12
-			nop; nop; nop; nop;
-			.set mips0
-			.set reorder
-		        " : "=r" (tmp1), "=r" (tmp2), "=r" (addr));
-		}
-
-		return 1;
-	} else {
-		if(mips_cputype == CPU_R5000)
-			return -1;
-		else
-			return 0;
-	}
-#else
-	/*
-	 * XXX For now we don't panic and assume that existing chipset
-	 * controlled caches are setup correnctly and are completly
-	 * transparent.  Works fine for those MIPS machines I know.
-	 * Morituri the salutant ...
-	 */
-	return 0;
-
-	panic("Cannot probe SCACHE on this machine.");
-#endif
-}
 
 /* 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 probe_scache(unsigned long config)
+__initfunc(static int probe_scache(unsigned long config))
 {
 	extern unsigned long stext;
 	unsigned long flags, addr, begin, end, pow2;
@@ -2644,7 +2571,7 @@
 	return 1;
 }
 
-static void setup_noscache_funcs(void)
+__initfunc(static void setup_noscache_funcs(void))
 {
 	unsigned int prid;
 
@@ -2677,18 +2604,16 @@
 		flush_page_to_ram = r4k_flush_page_to_ram_d32i32;
 		break;
 	}
-	flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_pc;
-	flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_pc;
+	dma_cache_wback_inv = r4k_dma_cache_wback_inv_pc;
+	dma_cache_inv = r4k_dma_cache_inv_pc;
 }
 
-static void setup_scache_funcs(void)
+__initfunc(static void setup_scache_funcs(void))
 {
 	switch(sc_lsize) {
 	case 16:
 		switch(dc_lsize) {
 		case 16:
-			clear_page = r4k_clear_page_d16;
-			copy_page = r4k_copy_page_d16;
 			flush_cache_all = r4k_flush_cache_all_s16d16i16;
 			flush_cache_mm = r4k_flush_cache_mm_s16d16i16;
 			flush_cache_range = r4k_flush_cache_range_s16d16i16;
@@ -2696,8 +2621,6 @@
 			flush_page_to_ram = r4k_flush_page_to_ram_s16d16i16;
 			break;
 		case 32:
-			clear_page = r4k_clear_page_d32;
-			copy_page = r4k_copy_page_d32;
 			flush_cache_all = r4k_flush_cache_all_s16d32i32;
 			flush_cache_mm = r4k_flush_cache_mm_s16d32i32;
 			flush_cache_range = r4k_flush_cache_range_s16d32i32;
@@ -2709,8 +2632,6 @@
 	case 32:
 		switch(dc_lsize) {
 		case 16:
-			clear_page = r4k_clear_page_d16;
-			copy_page = r4k_copy_page_d16;
 			flush_cache_all = r4k_flush_cache_all_s32d16i16;
 			flush_cache_mm = r4k_flush_cache_mm_s32d16i16;
 			flush_cache_range = r4k_flush_cache_range_s32d16i16;
@@ -2718,8 +2639,6 @@
 			flush_page_to_ram = r4k_flush_page_to_ram_s32d16i16;
 			break;
 		case 32:
-			clear_page = r4k_clear_page_d32;
-			copy_page = r4k_copy_page_d32;
 			flush_cache_all = r4k_flush_cache_all_s32d32i32;
 			flush_cache_mm = r4k_flush_cache_mm_s32d32i32;
 			flush_cache_range = r4k_flush_cache_range_s32d32i32;
@@ -2730,8 +2649,6 @@
 	case 64:
 		switch(dc_lsize) {
 		case 16:
-			clear_page = r4k_clear_page_d16;
-			copy_page = r4k_copy_page_d16;
 			flush_cache_all = r4k_flush_cache_all_s64d16i16;
 			flush_cache_mm = r4k_flush_cache_mm_s64d16i16;
 			flush_cache_range = r4k_flush_cache_range_s64d16i16;
@@ -2739,8 +2656,6 @@
 			flush_page_to_ram = r4k_flush_page_to_ram_s64d16i16;
 			break;
 		case 32:
-			clear_page = r4k_clear_page_d32;
-			copy_page = r4k_copy_page_d32;
 			flush_cache_all = r4k_flush_cache_all_s64d32i32;
 			flush_cache_mm = r4k_flush_cache_mm_s64d32i32;
 			flush_cache_range = r4k_flush_cache_range_s64d32i32;
@@ -2751,8 +2666,6 @@
 	case 128:
 		switch(dc_lsize) {
 		case 16:
-			clear_page = r4k_clear_page_d16;
-			copy_page = r4k_copy_page_d16;
 			flush_cache_all = r4k_flush_cache_all_s128d16i16;
 			flush_cache_mm = r4k_flush_cache_mm_s128d16i16;
 			flush_cache_range = r4k_flush_cache_range_s128d16i16;
@@ -2760,8 +2673,6 @@
 			flush_page_to_ram = r4k_flush_page_to_ram_s128d16i16;
 			break;
 		case 32:
-			clear_page = r4k_clear_page_d32;
-			copy_page = r4k_copy_page_d32;
 			flush_cache_all = r4k_flush_cache_all_s128d32i32;
 			flush_cache_mm = r4k_flush_cache_mm_s128d32i32;
 			flush_cache_range = r4k_flush_cache_range_s128d32i32;
@@ -2771,77 +2682,57 @@
 		};
 		break;
 	}
-
-	/* XXX Do these for Indy style caches also.  No need for now ... */
-	flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_sc;
-	flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_sc;
+	clear_page = r4k_clear_page;
+	copy_page = r4k_copy_page;
+	dma_cache_wback_inv = r4k_dma_cache_wback_inv_sc;
+	dma_cache_inv = r4k_dma_cache_inv_sc;
 }
 
 typedef int (*probe_func_t)(unsigned long);
-static probe_func_t probe_scache_kseg1;
 
-void ld_mmu_r4xx0(void)
+__initfunc(static inline void setup_scache(unsigned int config))
 {
-	unsigned long cfg = read_32bit_cp0_register(CP0_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) {
+		setup_scache_funcs();
+		return;
+	}
+
+	setup_noscache_funcs();
+}
+
+static int r4k_user_mode(struct pt_regs *regs)
+{
+	return (regs->cp0_status & ST0_KSU) == KSU_USER;
+}
+
+
+__initfunc(void ld_mmu_r4xx0(void))
+{
+	unsigned long config = read_32bit_cp0_register(CP0_CONFIG);
+
 	printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID));
 
 	set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NONCOHERENT);
-//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_WA);
-//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NO_WA);
 
-	probe_icache(cfg);
-	probe_dcache(cfg);
+	probe_icache(config);
+	probe_dcache(config);
+	setup_scache(config);
 
 	switch(mips_cputype) {
-	case CPU_R4000PC:
-	case CPU_R4000SC:
-	case CPU_R4000MC:
-	case CPU_R4400PC:
-	case CPU_R4400SC:
-	case CPU_R4400MC:
-		probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache));
-		sc_present = probe_scache_kseg1(cfg);
-		break;
-
-	case CPU_R4600:
-	case CPU_R4640:
+	case CPU_R4600:			/* QED style two way caches? */
 	case CPU_R4700:
-	case CPU_R5000:	/* XXX: We don't handle the true R5000 SCACHE */
+	case CPU_R5000:
 	case CPU_NEVADA:
-		probe_scache_kseg1 = (probe_func_t)
-			(KSEG1ADDR(&probe_scache_eeprom));
-		sc_present = probe_scache_eeprom(cfg);
-
-		/* Try using tags if eeprom gives us bogus data. */
-		if(sc_present == -1) {
-			probe_scache_kseg1 =
-				(probe_func_t) (KSEG1ADDR(&probe_scache));
-			sc_present = probe_scache_kseg1(cfg);
-		}
-		break;
-	};
-
-	if(sc_present == 1
-	   && (mips_cputype == CPU_R4000SC
-               || mips_cputype == CPU_R4000MC
-               || mips_cputype == CPU_R4400SC
-               || mips_cputype == CPU_R4400MC)) {
-		/* Has a true secondary cache. */
-		setup_scache_funcs();
-	} else {
-		/* Lacks true secondary cache. */
-		setup_noscache_funcs();
-		if((mips_cputype != CPU_R5000)) { /* XXX */
-			flush_cache_page =
-				r4k_flush_cache_page_d32i32_r4600;
-			flush_page_to_ram =
-				r4k_flush_page_to_ram_d32i32_r4600;
-		}
+		flush_cache_page = r4k_flush_cache_page_d32i32_r4600;
 	}
 
-	/* XXX Handle true second level cache w/ split I/D */
 	flush_cache_sigtramp = r4k_flush_cache_sigtramp;
 	if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2020) {
 		flush_cache_sigtramp = r4600v20k_flush_cache_sigtramp;
@@ -2859,6 +2750,8 @@
 	show_regs = r4k_show_regs;
     
         add_wired_entry = r4k_add_wired_entry;
+
+	user_mode = r4k_user_mode;
 
 	flush_cache_all();
 	write_32bit_cp0_register(CP0_WIRED, 0);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov