[MIPS] R4000/R4400 errata workarounds

 This is the gereric part of R4000/R4400 errata workarounds.  They include 
compiler and assembler support as well as some source code modifications 
to address the problems with some combinations of multiply/divide+shift 
instructions as well as the daddi and daddiu instructions.

 Changes included are as follows:

1. New Kconfig options to select workarounds by platforms as necessary.

2. Arch top-level Makefile to pass necessary options to the compiler; also 
   incompatible configurations are detected (-mno-sym32 unsupported as 
   horribly intrusive for little gain).

3. Bug detection updated and shuffled -- the multiply/divide+shift problem 
   is lethal enough that if not worked around it makes the kernel crash in 
   time_init() because of a division by zero; the daddiu erratum might 
   also trigger early potentially, though I have not observed it.  On the 
   other hand the daddi detection code requires the exception subsystem to 
   have been initialised (and is there mainly for information).

4. r4k_daddiu_bug() added so that the existence of the erratum can be 
   queried by code at the run time as necessary; useful for generated code 
   like TLB fault and copy/clear page handlers.

5. __udelay() updated as it uses multiplication in inline assembly.

 Note that -mdaddi requires modified toolchain (which has been maintained 
by myself and available from my site for ~4years now -- versions covered 
are GCC 2.95.4 - 4.1.2 and binutils from 2.13 onwards).  The -mfix-r4000 
and -mfix-r4400 have been standard for a while though.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/kernel/cpu-bugs64.c b/arch/mips/kernel/cpu-bugs64.c
index af78456..417bb3e 100644
--- a/arch/mips/kernel/cpu-bugs64.c
+++ b/arch/mips/kernel/cpu-bugs64.c
@@ -18,6 +18,15 @@
 #include <asm/mipsregs.h>
 #include <asm/system.h>
 
+static char bug64hit[] __initdata =
+	"reliable operation impossible!\n%s";
+static char nowar[] __initdata =
+	"Please report to <linux-mips@linux-mips.org>.";
+static char r4kwar[] __initdata =
+	"Enable CPU_R4000_WORKAROUNDS to rectify.";
+static char daddiwar[] __initdata =
+	"Enable CPU_DADDI_WORKAROUNDS to rectify.";
+
 static inline void align_mod(const int align, const int mod)
 {
 	asm volatile(
@@ -155,13 +164,7 @@
 	}
 
 	printk("no.\n");
-	panic("Reliable operation impossible!\n"
-#ifndef CONFIG_CPU_R4000
-	      "Configure for R4000 to enable the workaround."
-#else
-	      "Please report to <linux-mips@linux-mips.org>."
-#endif
-	      );
+	panic(bug64hit, !R4000_WAR ? r4kwar : nowar);
 }
 
 static volatile int daddi_ov __initdata = 0;
@@ -233,15 +236,11 @@
 	}
 
 	printk("no.\n");
-	panic("Reliable operation impossible!\n"
-#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
-	      "Configure for R4000 or R4400 to enable the workaround."
-#else
-	      "Please report to <linux-mips@linux-mips.org>."
-#endif
-	      );
+	panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
 }
 
+int daddiu_bug __initdata = -1;
+
 static inline void check_daddiu(void)
 {
 	long v, w, tmp;
@@ -281,7 +280,9 @@
 		: "=&r" (v), "=&r" (w), "=&r" (tmp)
 		: "I" (0xffffffffffffdb9aUL), "I" (0x1234));
 
-	if (v == w) {
+	daddiu_bug = v != w;
+
+	if (!daddiu_bug) {
 		printk("no.\n");
 		return;
 	}
@@ -303,18 +304,16 @@
 	}
 
 	printk("no.\n");
-	panic("Reliable operation impossible!\n"
-#if !defined(CONFIG_CPU_R4000) && !defined(CONFIG_CPU_R4400)
-	      "Configure for R4000 or R4400 to enable the workaround."
-#else
-	      "Please report to <linux-mips@linux-mips.org>."
-#endif
-	      );
+	panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
+}
+
+void __init check_bugs64_early(void)
+{
+	check_mult_sh();
+	check_daddiu();
 }
 
 void __init check_bugs64(void)
 {
-	check_mult_sh();
 	check_daddi();
-	check_daddiu();
 }