[MIPS] MIPS32/MIPS64 secondary cache management
    
Signed-off-by: Chris Dearman <chris@mips.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 05663e5..bc6a8f97 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -325,6 +325,7 @@
 	select I8259
 	select MIPS_BOARDS_GEN
 	select MIPS_BONITO64
+	select MIPS_CPU_SCACHE
 	select MIPS_GT64120
 	select MIPS_MSC
 	select SWAP_IO_SPACE
@@ -1478,6 +1479,13 @@
 	bool
 	select BOARD_SCACHE
 
+#
+# Support for a MIPS32 / MIPS64 style S-caches
+#
+config MIPS_CPU_SCACHE
+	bool
+	select BOARD_SCACHE
+
 config R5000_CPU_SCACHE
 	bool
 	select BOARD_SCACHE
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 8c2c359..e045aba 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -597,8 +597,6 @@
 		break;
 	case PRID_IMP_25KF:
 		c->cputype = CPU_25KF;
-		/* Probe for L2 cache */
-		c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
 		break;
 	case PRID_IMP_34K:
 		c->cputype = CPU_34K;
diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile
index 4a622011..19e41fd 100644
--- a/arch/mips/mm/Makefile
+++ b/arch/mips/mm/Makefile
@@ -30,6 +30,7 @@
 obj-$(CONFIG_IP22_CPU_SCACHE)	+= sc-ip22.o
 obj-$(CONFIG_R5000_CPU_SCACHE)  += sc-r5k.o
 obj-$(CONFIG_RM7000_CPU_SCACHE)	+= sc-rm7k.o
+obj-$(CONFIG_MIPS_CPU_SCACHE)	+= sc-mips.o
 
 #
 # Choose one DMA coherency model
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 4a43924..0145093 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -1092,6 +1092,7 @@
 
 extern int r5k_sc_init(void);
 extern int rm7k_sc_init(void);
+extern int mips_sc_init(void);
 
 static void __init setup_scache(void)
 {
@@ -1139,17 +1140,29 @@
 		return;
 
 	default:
+		if (c->isa_level == MIPS_CPU_ISA_M32R1 ||
+		    c->isa_level == MIPS_CPU_ISA_M32R2 ||
+		    c->isa_level == MIPS_CPU_ISA_M64R1 ||
+		    c->isa_level == MIPS_CPU_ISA_M64R2) {
+#ifdef CONFIG_MIPS_CPU_SCACHE
+			if (mips_sc_init ()) {
+				scache_size = c->scache.ways * c->scache.sets * c->scache.linesz;
+				printk("MIPS secondary cache %ldkB, %s, linesize %d bytes.\n",
+				       scache_size >> 10,
+				       way_string[c->scache.ways], c->scache.linesz);
+			}
+#else
+			if (!(c->scache.flags & MIPS_CACHE_NOT_PRESENT))
+				panic("Dunno how to handle MIPS32 / MIPS64 second level cache");
+#endif
+			return;
+		}
 		sc_present = 0;
 	}
 
 	if (!sc_present)
 		return;
 
-	if ((c->isa_level == MIPS_CPU_ISA_M32R1 ||
-	     c->isa_level == MIPS_CPU_ISA_M64R1) &&
-	    !(c->scache.flags & MIPS_CACHE_NOT_PRESENT))
-		panic("Dunno how to handle MIPS32 / MIPS64 second level cache");
-
 	/* compute a couple of other cache variables */
 	c->scache.waysize = scache_size / c->scache.ways;
 
diff --git a/arch/mips/mm/sc-mips.c b/arch/mips/mm/sc-mips.c
new file mode 100644
index 0000000..d3f92a9
--- /dev/null
+++ b/arch/mips/mm/sc-mips.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2006 Chris Dearman (chris@mips.com),
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/mipsregs.h>
+#include <asm/bcache.h>
+#include <asm/cacheops.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/r4kcache.h>
+
+/*
+ * MIPS32/MIPS64 L2 cache handling
+ */
+
+/*
+ * Writeback and invalidate the secondary cache before DMA.
+ */
+static void mips_sc_wback_inv(unsigned long addr, unsigned long size)
+{
+	unsigned long sc_lsize = cpu_scache_line_size();
+	unsigned long end, a;
+
+	pr_debug("mips_sc_wback_inv[%08lx,%08lx]", addr, size);
+
+	/* Catch bad driver code */
+	BUG_ON(size == 0);
+
+	a = addr & ~(sc_lsize - 1);
+	end = (addr + size - 1) & ~(sc_lsize - 1);
+	while (1) {
+		flush_scache_line(a);		/* Hit_Writeback_Inv_SD */
+		if (a == end)
+			break;
+		a += sc_lsize;
+	}
+}
+
+/*
+ * Invalidate the secondary cache before DMA.
+ */
+static void mips_sc_inv(unsigned long addr, unsigned long size)
+{
+	unsigned long sc_lsize = cpu_scache_line_size();
+	unsigned long end, a;
+
+	pr_debug("mips_sc_inv[%08lx,%08lx]", addr, size);
+
+	/* Catch bad driver code */
+	BUG_ON(size == 0);
+
+	a = addr & ~(sc_lsize - 1);
+	end = (addr + size - 1) & ~(sc_lsize - 1);
+	while (1) {
+		invalidate_scache_line(a);	/* Hit_Invalidate_SD */
+		if (a == end)
+			break;
+		a += sc_lsize;
+	}
+}
+
+static void mips_sc_enable(void)
+{
+	/* L2 cache is permanently enabled */
+}
+
+static void mips_sc_disable(void)
+{
+	/* L2 cache is permanently enabled */
+}
+
+static struct bcache_ops mips_sc_ops = {
+	.bc_enable = mips_sc_enable,
+	.bc_disable = mips_sc_disable,
+	.bc_wback_inv = mips_sc_wback_inv,
+	.bc_inv = mips_sc_inv
+};
+
+static inline int __init mips_sc_probe(void)
+{
+	struct cpuinfo_mips *c = &current_cpu_data;
+	unsigned int config1, config2;
+	unsigned int tmp;
+
+	/* Mark as not present until probe completed */
+	c->scache.flags |= MIPS_CACHE_NOT_PRESENT;
+
+	/* Ignore anything but MIPSxx processors */
+	if (c->isa_level != MIPS_CPU_ISA_M32R1 &&
+	    c->isa_level != MIPS_CPU_ISA_M32R2 &&
+	    c->isa_level != MIPS_CPU_ISA_M64R1 &&
+	    c->isa_level != MIPS_CPU_ISA_M64R2)
+		return 0;
+
+	/* Does this MIPS32/MIPS64 CPU have a config2 register? */
+	config1 = read_c0_config1();
+	if (!(config1 & MIPS_CONF_M))
+		return 0;
+
+	config2 = read_c0_config2();
+	tmp = (config2 >> 4) & 0x0f;
+	if (0 < tmp && tmp <= 7)
+		c->scache.linesz = 2 << tmp;
+	else
+		return 0;
+
+	tmp = (config2 >> 8) & 0x0f;
+	if (0 <= tmp && tmp <= 7)
+		c->scache.sets = 64 << tmp;
+	else
+		return 0;
+
+	tmp = (config2 >> 0) & 0x0f;
+	if (0 <= tmp && tmp <= 7)
+		c->scache.ways = tmp + 1;
+	else
+		return 0;
+
+	c->scache.waysize = c->scache.sets * c->scache.linesz;
+
+	c->scache.flags &= ~MIPS_CACHE_NOT_PRESENT;
+
+	return 1;
+}
+
+int __init mips_sc_init(void)
+{
+	int found = mips_sc_probe ();
+	if (found) {
+		mips_sc_enable();
+		bcops = &mips_sc_ops;
+	}
+	return found;
+}
+