[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/include/asm-mips/bugs.h b/include/asm-mips/bugs.h
index 0d7f9c1..9dc10df 100644
--- a/include/asm-mips/bugs.h
+++ b/include/asm-mips/bugs.h
@@ -1,19 +1,34 @@
 /*
  * This is included by init/main.c to check for architecture-dependent bugs.
  *
+ * Copyright (C) 2007  Maciej W. Rozycki
+ *
  * Needs:
  *	void check_bugs(void);
  */
 #ifndef _ASM_BUGS_H
 #define _ASM_BUGS_H
 
+#include <linux/bug.h>
 #include <linux/delay.h>
+
 #include <asm/cpu.h>
 #include <asm/cpu-info.h>
 
+extern int daddiu_bug;
+
+extern void check_bugs64_early(void);
+
 extern void check_bugs32(void);
 extern void check_bugs64(void);
 
+static inline void check_bugs_early(void)
+{
+#ifdef CONFIG_64BIT
+	check_bugs64_early();
+#endif
+}
+
 static inline void check_bugs(void)
 {
 	unsigned int cpu = smp_processor_id();
@@ -25,4 +40,14 @@
 #endif
 }
 
+static inline int r4k_daddiu_bug(void)
+{
+#ifdef CONFIG_64BIT
+	WARN_ON(daddiu_bug < 0);
+	return daddiu_bug != 0;
+#else
+	return 0;
+#endif
+}
+
 #endif /* _ASM_BUGS_H */
diff --git a/include/asm-mips/delay.h b/include/asm-mips/delay.h
index fab3213..de5105d 100644
--- a/include/asm-mips/delay.h
+++ b/include/asm-mips/delay.h
@@ -6,13 +6,16 @@
  * Copyright (C) 1994 by Waldorf Electronics
  * Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle
  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 2007  Maciej W. Rozycki
  */
 #ifndef _ASM_DELAY_H
 #define _ASM_DELAY_H
 
 #include <linux/param.h>
 #include <linux/smp.h>
+
 #include <asm/compiler.h>
+#include <asm/war.h>
 
 static inline void __delay(unsigned long loops)
 {
@@ -50,7 +53,7 @@
 
 static inline void __udelay(unsigned long usecs, unsigned long lpj)
 {
-	unsigned long lo;
+	unsigned long hi, lo;
 
 	/*
 	 * The rates of 128 is rounded wrongly by the catchall case
@@ -70,11 +73,16 @@
 		: "=h" (usecs), "=l" (lo)
 		: "r" (usecs), "r" (lpj)
 		: GCC_REG_ACCUM);
-	else if (sizeof(long) == 8)
+	else if (sizeof(long) == 8 && !R4000_WAR)
 		__asm__("dmultu\t%2, %3"
 		: "=h" (usecs), "=l" (lo)
 		: "r" (usecs), "r" (lpj)
 		: GCC_REG_ACCUM);
+	else if (sizeof(long) == 8 && R4000_WAR)
+		__asm__("dmultu\t%3, %4\n\tmfhi\t%0"
+		: "=r" (usecs), "=h" (hi), "=l" (lo)
+		: "r" (usecs), "r" (lpj)
+		: GCC_REG_ACCUM);
 
 	__delay(usecs);
 }
diff --git a/include/asm-mips/war.h b/include/asm-mips/war.h
index d2808ed..22361d5 100644
--- a/include/asm-mips/war.h
+++ b/include/asm-mips/war.h
@@ -4,6 +4,7 @@
  * for more details.
  *
  * Copyright (C) 2002, 2004, 2007 by Ralf Baechle
+ * Copyright (C) 2007  Maciej W. Rozycki
  */
 #ifndef _ASM_WAR_H
 #define _ASM_WAR_H
@@ -11,6 +12,67 @@
 #include <war.h>
 
 /*
+ * Work around certain R4000 CPU errata (as implemented by GCC):
+ *
+ * - A double-word or a variable shift may give an incorrect result
+ *   if executed immediately after starting an integer division:
+ *   "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
+ *   erratum #28
+ *   "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0", erratum
+ *   #19
+ *
+ * - A double-word or a variable shift may give an incorrect result
+ *   if executed while an integer multiplication is in progress:
+ *   "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
+ *   errata #16 & #28
+ *
+ * - An integer division may give an incorrect result if started in
+ *   a delay slot of a taken branch or a jump:
+ *   "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
+ *   erratum #52
+ */
+#ifdef CONFIG_CPU_R4000_WORKAROUNDS
+#define R4000_WAR 1
+#else
+#define R4000_WAR 0
+#endif
+
+/*
+ * Work around certain R4400 CPU errata (as implemented by GCC):
+ *
+ * - A double-word or a variable shift may give an incorrect result
+ *   if executed immediately after starting an integer division:
+ *   "MIPS R4400MC Errata, Processor Revision 1.0", erratum #10
+ *   "MIPS R4400MC Errata, Processor Revision 2.0 & 3.0", erratum #4
+ */
+#ifdef CONFIG_CPU_R4400_WORKAROUNDS
+#define R4400_WAR 1
+#else
+#define R4400_WAR 0
+#endif
+
+/*
+ * Work around the "daddi" and "daddiu" CPU errata:
+ *
+ * - The `daddi' instruction fails to trap on overflow.
+ *   "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
+ *   erratum #23
+ *
+ * - The `daddiu' instruction can produce an incorrect result.
+ *   "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0",
+ *   erratum #41
+ *   "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0", erratum
+ *   #15
+ *   "MIPS R4400PC/SC Errata, Processor Revision 1.0", erratum #7
+ *   "MIPS R4400MC Errata, Processor Revision 1.0", erratum #5
+ */
+#ifdef CONFIG_CPU_DADDI_WORKAROUNDS
+#define DADDI_WAR 1
+#else
+#define DADDI_WAR 0
+#endif
+
+/*
  * Another R4600 erratum.  Due to the lack of errata information the exact
  * technical details aren't known.  I've experimentally found that disabling
  * interrupts during indexed I-cache flushes seems to be sufficient to deal