Better interface to run uncached cache setup code.

Signed-off-by: Thiemo Seufer <ths@networkno.de>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 0373034..cf12caf 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -2,8 +2,8 @@
 # Makefile for MIPS-specific library files..
 #
 
-lib-y	+= csum_partial_copy.o memcpy.o promlib.o \
-	   strlen_user.o strncpy_user.o strnlen_user.o
+lib-y	+= csum_partial_copy.o memcpy.o promlib.o strlen_user.o strncpy_user.o \
+	   strnlen_user.o uncached.o
 
 obj-y	+= iomap.o
 
diff --git a/arch/mips/lib/uncached.c b/arch/mips/lib/uncached.c
new file mode 100644
index 0000000..98ce89f
--- /dev/null
+++ b/arch/mips/lib/uncached.c
@@ -0,0 +1,76 @@
+/*
+ * 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) 2005 Thiemo Seufer
+ * Copyright (C) 2005  MIPS Technologies, Inc.  All rights reserved.
+ *	Author: Maciej W. Rozycki <macro@mips.com>
+ */
+
+#include <linux/init.h>
+
+#include <asm/addrspace.h>
+#include <asm/bug.h>
+
+#ifndef CKSEG2
+#define CKSEG2 CKSSEG
+#endif
+#ifndef TO_PHYS_MASK
+#define TO_PHYS_MASK -1
+#endif
+
+/*
+ * FUNC is executed in one of the uncached segments, depending on its
+ * original address as follows:
+ *
+ * 1. If the original address is in CKSEG0 or CKSEG1, then the uncached
+ *    segment used is CKSEG1.
+ * 2. If the original address is in XKPHYS, then the uncached segment
+ *    used is XKPHYS(2).
+ * 3. Otherwise it's a bug.
+ *
+ * The same remapping is done with the stack pointer.  Stack handling
+ * works because we don't handle stack arguments or more complex return
+ * values, so we can avoid sharing the same stack area between a cached
+ * and the uncached mode.
+ */
+unsigned long __init run_uncached(void *func)
+{
+	register long sp __asm__("$sp");
+	register long ret __asm__("$2");
+	long lfunc = (long)func, ufunc;
+	long usp;
+
+	if (sp >= (long)CKSEG0 && sp < (long)CKSEG2)
+		usp = CKSEG1ADDR(sp);
+	else if ((long long)sp >= (long long)PHYS_TO_XKPHYS(0LL, 0) &&
+		 (long long)sp < (long long)PHYS_TO_XKPHYS(8LL, 0))
+		usp = PHYS_TO_XKPHYS((long long)K_CALG_UNCACHED,
+				     XKPHYS_TO_PHYS((long long)sp));
+	else {
+		BUG();
+		usp = sp;
+	}
+	if (lfunc >= (long)CKSEG0 && lfunc < (long)CKSEG2)
+		ufunc = CKSEG1ADDR(lfunc);
+	else if ((long long)lfunc >= (long long)PHYS_TO_XKPHYS(0LL, 0) &&
+		 (long long)lfunc < (long long)PHYS_TO_XKPHYS(8LL, 0))
+		ufunc = PHYS_TO_XKPHYS((long long)K_CALG_UNCACHED,
+				       XKPHYS_TO_PHYS((long long)lfunc));
+	else {
+		BUG();
+		ufunc = lfunc;
+	}
+
+	__asm__ __volatile__ (
+		"	move	$16, $sp\n"
+		"	move	$sp, %1\n"
+		"	jalr	%2\n"
+		"	move	$sp, $16"
+		: "=r" (ret)
+		: "r" (usp), "r" (ufunc)
+		: "$16", "$31");
+
+	return ret;
+}
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 48d731c..b901473 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -26,6 +26,7 @@
 #include <asm/system.h>
 #include <asm/mmu_context.h>
 #include <asm/war.h>
+#include <asm/cacheflush.h> /* for run_uncached() */
 
 static unsigned long icache_size, dcache_size, scache_size;
 
@@ -1119,7 +1120,6 @@
 	return 1;
 }
 
-typedef int (*probe_func_t)(unsigned long);
 extern int r5k_sc_init(void);
 extern int rm7k_sc_init(void);
 
@@ -1127,7 +1127,6 @@
 {
 	struct cpuinfo_mips *c = &current_cpu_data;
 	unsigned int config = read_c0_config();
-	probe_func_t probe_scache_kseg1;
 	int sc_present = 0;
 
 	/*
@@ -1140,8 +1139,7 @@
 	case CPU_R4000MC:
 	case CPU_R4400SC:
 	case CPU_R4400MC:
-		probe_scache_kseg1 = (probe_func_t) (CKSEG1ADDR(&probe_scache));
-		sc_present = probe_scache_kseg1(config);
+		sc_present = run_uncached(probe_scache);
 		if (sc_present)
 			c->options |= MIPS_CPU_CACHE_CDEX_S;
 		break;
diff --git a/arch/mips/mm/sc-rm7k.c b/arch/mips/mm/sc-rm7k.c
index 4e92f93..1df5aab 100644
--- a/arch/mips/mm/sc-rm7k.c
+++ b/arch/mips/mm/sc-rm7k.c
@@ -15,6 +15,7 @@
 #include <asm/cacheops.h>
 #include <asm/mipsregs.h>
 #include <asm/processor.h>
+#include <asm/cacheflush.h> /* for run_uncached() */
 
 /* Primary cache parameters. */
 #define sc_lsize	32
@@ -96,25 +97,13 @@
 }
 
 /*
- * This function is executed in the uncached segment CKSEG1.
- * It must not touch the stack, because the stack pointer still points
- * into CKSEG0.
- *
- * Three options:
- *	- Write it in assembly and guarantee that we don't use the stack.
- *	- Disable caching for CKSEG0 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 c-r4k.c
- *
- * It seems we get our kicks from relying on unguaranteed behaviour in GCC
+ * This function is executed in uncached address space.
  */
 static __init void __rm7k_sc_enable(void)
 {
 	int i;
 
-	set_c0_config(1 << 3);				/* CONF_SE */
+	set_c0_config(R7K_CONF_SE);
 
 	write_c0_taglo(0);
 	write_c0_taghi(0);
@@ -127,24 +116,22 @@
 		      ".set mips0\n\t"
 		      ".set reorder"
 		      :
-		      : "r" (KSEG0ADDR(i)), "i" (Index_Store_Tag_SD));
+		      : "r" (CKSEG0ADDR(i)), "i" (Index_Store_Tag_SD));
 	}
 }
 
 static __init void rm7k_sc_enable(void)
 {
-	void (*func)(void) = (void *) KSEG1ADDR(&__rm7k_sc_enable);
-
-	if (read_c0_config() & 0x08)			/* CONF_SE */
+	if (read_c0_config() & R7K_CONF_SE)
 		return;
 
 	printk(KERN_INFO "Enabling secondary cache...");
-	func();
+	run_uncached(__rm7k_sc_enable);
 }
 
 static void rm7k_sc_disable(void)
 {
-	clear_c0_config(1<<3);				/* CONF_SE */
+	clear_c0_config(R7K_CONF_SE);
 }
 
 struct bcache_ops rm7k_sc_ops = {
@@ -164,7 +151,7 @@
 	printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n",
 	       (scache_size >> 10), sc_lsize);
 
-	if (!((config >> 3) & 1))	/* CONF_SE */
+	if (!(config & R7K_CONF_SE))
 		rm7k_sc_enable();
 
 	/*
diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h
index 4213d2c..a18ba2e 100644
--- a/include/asm-mips/cacheflush.h
+++ b/include/asm-mips/cacheflush.h
@@ -90,4 +90,7 @@
 #define ClearPageDcacheDirty(page)	\
 	clear_bit(PG_dcache_dirty, &(page)->flags)
 
+/* Run kernel code uncached, useful for cache probing functions. */
+unsigned long __init run_uncached(void *func);
+
 #endif /* _ASM_CACHEFLUSH_H */
diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h
index 006354e..f3b0b41 100644
--- a/include/asm-mips/mipsregs.h
+++ b/include/asm-mips/mipsregs.h
@@ -433,6 +433,9 @@
 #define R5K_CONF_SE		(_ULCAST_(1) << 12)
 #define R5K_CONF_SS		(_ULCAST_(3) << 20)
 
+/* Bits specific to the RM7000.  */
+#define R7K_CONF_SE		(_ULCAST_(1) << 3)
+
 /* Bits specific to the R10000.  */
 #define R10K_CONF_DN		(_ULCAST_(3) <<  3)
 #define R10K_CONF_CT		(_ULCAST_(1) <<  5)