Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=20d60d9973c3b441902b0a3f4f6f7e7ade08f77d
Commit:     20d60d9973c3b441902b0a3f4f6f7e7ade08f77d
Parent:     2b22c034d04d3632a339d14d5803c8f94e412608
Author:     Maciej W. Rozycki <[EMAIL PROTECTED]>
AuthorDate: Tue Oct 23 12:43:11 2007 +0100
Committer:  Ralf Baechle <[EMAIL PROTECTED]>
CommitDate: Tue Jan 29 10:14:54 2008 +0000

    [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 <[EMAIL PROTECTED]>
    Signed-off-by: Ralf Baechle <[EMAIL PROTECTED]>
---
 arch/mips/Kconfig             |   16 ++++++++++
 arch/mips/Makefile            |   12 ++++++--
 arch/mips/kernel/cpu-bugs64.c |   47 +++++++++++++++----------------
 arch/mips/kernel/setup.c      |    4 ++-
 include/asm-mips/bugs.h       |   25 ++++++++++++++++
 include/asm-mips/delay.h      |   12 ++++++-
 include/asm-mips/war.h        |   62 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 148 insertions(+), 30 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 12bf963..11bc17c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -91,6 +91,9 @@ config MACH_DECSTATION
        select BOOT_ELF32
        select CEVT_R4K
        select CSRC_R4K
+       select CPU_DADDI_WORKAROUNDS if 64BIT
+       select CPU_R4000_WORKAROUNDS if 64BIT
+       select CPU_R4400_WORKAROUNDS if 64BIT
        select DMA_NONCOHERENT
        select NO_IOPORT
        select IRQ_CPU
@@ -1606,6 +1609,19 @@ config GENERIC_CLOCKEVENTS_BROADCAST
        bool
 
 #
+# CPU non-features
+#
+config CPU_DADDI_WORKAROUNDS
+       bool
+
+config CPU_R4000_WORKAROUNDS
+       bool
+       select CPU_R4400_WORKAROUNDS
+
+config CPU_R4400_WORKAROUNDS
+       bool
+
+#
 # Use the generic interrupt handling code in kernel/irq/:
 #
 config GENERIC_HARDIRQS
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index a1f8d8b..a49127a 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -141,6 +141,10 @@ cflags-$(CONFIG_CPU_R8000) += -march=r8000 -Wa,--trap
 cflags-$(CONFIG_CPU_R10000)    += $(call cc-option,-march=r10000,-march=r8000) 
\
                        -Wa,--trap
 
+cflags-$(CONFIG_CPU_R4000_WORKAROUNDS) += $(call cc-option,-mfix-r4000,)
+cflags-$(CONFIG_CPU_R4400_WORKAROUNDS) += $(call cc-option,-mfix-r4400,)
+cflags-$(CONFIG_CPU_DADDI_WORKAROUNDS) += $(call cc-option,-mno-daddi,)
+
 ifdef CONFIG_CPU_SB1
 ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
 MODFLAGS       += -msb1-pass1-workarounds
@@ -602,9 +606,11 @@ ifdef CONFIG_64BIT
     endif
   endif
 
-  ifeq ($(KBUILD_SYM32), y)
-    ifeq ($(call cc-option-yn,-msym32), y)
-      cflags-y += -msym32 -DKBUILD_64BIT_SYM32
+  ifeq ($(KBUILD_SYM32)$(call cc-option-yn,-msym32), yy)
+    cflags-y += -msym32 -DKBUILD_64BIT_SYM32
+  else
+    ifeq ($(CONFIG_CPU_DADDI_WORKAROUNDS), y)
+      $(error CONFIG_CPU_DADDI_WORKAROUNDS unsupported without -msym32)
     endif
   endif
 endif
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 <[EMAIL PROTECTED]>.";
+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 @@ static inline void check_mult_sh(void)
        }
 
        printk("no.\n");
-       panic("Reliable operation impossible!\n"
-#ifndef CONFIG_CPU_R4000
-             "Configure for R4000 to enable the workaround."
-#else
-             "Please report to <[EMAIL PROTECTED]>."
-#endif
-             );
+       panic(bug64hit, !R4000_WAR ? r4kwar : nowar);
 }
 
 static volatile int daddi_ov __initdata = 0;
@@ -233,15 +236,11 @@ static inline void check_daddi(void)
        }
 
        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 <[EMAIL PROTECTED]>."
-#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 @@ static inline void check_daddiu(void)
                : "=&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 @@ static inline void check_daddiu(void)
        }
 
        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 <[EMAIL PROTECTED]>."
-#endif
-             );
+       panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
 }
 
-void __init check_bugs64(void)
+void __init check_bugs64_early(void)
 {
        check_mult_sh();
-       check_daddi();
        check_daddiu();
 }
+
+void __init check_bugs64(void)
+{
+       check_daddi();
+}
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index f8a535a..7b4418d 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -8,7 +8,7 @@
  * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 01, 02, 03  Ralf Baechle
  * Copyright (C) 1996 Stoned Elipot
  * Copyright (C) 1999 Silicon Graphics, Inc.
- * Copyright (C) 2000 2001, 2002  Maciej W. Rozycki
+ * Copyright (C) 2000, 2001, 2002, 2007  Maciej W. Rozycki
  */
 #include <linux/init.h>
 #include <linux/ioport.h>
@@ -24,6 +24,7 @@
 
 #include <asm/addrspace.h>
 #include <asm/bootinfo.h>
+#include <asm/bugs.h>
 #include <asm/cache.h>
 #include <asm/cpu.h>
 #include <asm/sections.h>
@@ -561,6 +562,7 @@ void __init setup_arch(char **cmdline_p)
        }
 #endif
        cpu_report();
+       check_bugs_early();
 
 #if defined(CONFIG_VT)
 #if defined(CONFIG_VGA_CONSOLE)
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 @@ static inline void check_bugs(void)
 #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 __delay(unsigned long loops)
 
 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 @@ static inline void __udelay(unsigned long usecs, unsigned 
long lpj)
                : "=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
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to