MIPS: Do not fiddle with FRE unless FRE is actually available.
[deliverable/linux.git] / arch / mips / include / asm / fpu.h
index dd562414cd5effc97f834da299c052d2922e325a..5528f4e2af6a72f882e1d839039d441adb25ae92 100644 (file)
@@ -36,14 +36,16 @@ extern void _restore_fp(struct task_struct *);
 
 /*
  * This enum specifies a mode in which we want the FPU to operate, for cores
- * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
- * purposefully have the values 0 & 1 respectively, so that an integer value
- * of Status.FR can be trivially casted to the corresponding enum fpu_mode.
+ * which implement the Status.FR bit. Note that the bottom bit of the value
+ * purposefully matches the desired value of the Status.FR bit.
  */
 enum fpu_mode {
        FPU_32BIT = 0,          /* FR = 0 */
-       FPU_64BIT,              /* FR = 1 */
+       FPU_64BIT,              /* FR = 1, FRE = 0 */
        FPU_AS_IS,
+       FPU_HYBRID,             /* FR = 1, FRE = 1 */
+
+#define FPU_FR_MASK            0x1
 };
 
 static inline int __enable_fpu(enum fpu_mode mode)
@@ -57,6 +59,14 @@ static inline int __enable_fpu(enum fpu_mode mode)
                enable_fpu_hazard();
                return 0;
 
+       case FPU_HYBRID:
+               if (!cpu_has_fre)
+                       return SIGFPE;
+
+               /* set FRE */
+               write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
+               goto fr_common;
+
        case FPU_64BIT:
 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
                /* we only have a 32-bit FPU */
@@ -64,8 +74,13 @@ static inline int __enable_fpu(enum fpu_mode mode)
 #endif
                /* fall through */
        case FPU_32BIT:
+               if (cpu_has_fre) {
+                       /* clear FRE */
+                       write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
+               }
+fr_common:
                /* set CU1 & change FR appropriately */
-               fr = (int)mode;
+               fr = (int)mode & FPU_FR_MASK;
                change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
                enable_fpu_hazard();
 
@@ -102,13 +117,17 @@ static inline int __own_fpu(void)
        enum fpu_mode mode;
        int ret;
 
-       mode = !test_thread_flag(TIF_32BIT_FPREGS);
+       if (test_thread_flag(TIF_HYBRID_FPREGS))
+               mode = FPU_HYBRID;
+       else
+               mode = !test_thread_flag(TIF_32BIT_FPREGS);
+
        ret = __enable_fpu(mode);
        if (ret)
                return ret;
 
        KSTK_STATUS(current) |= ST0_CU1;
-       if (mode == FPU_64BIT)
+       if (mode == FPU_64BIT || mode == FPU_HYBRID)
                KSTK_STATUS(current) |= ST0_FR;
        else /* mode == FPU_32BIT */
                KSTK_STATUS(current) &= ~ST0_FR;
@@ -165,9 +184,34 @@ static inline int init_fpu(void)
        int ret = 0;
 
        if (cpu_has_fpu) {
+               unsigned int config5;
+
                ret = __own_fpu();
-               if (!ret)
+               if (ret)
+                       return ret;
+
+               if (!cpu_has_fre) {
                        _init_fpu();
+
+                       return 0;
+               }
+
+               config5 = read_c0_config5();
+
+               /*
+                * Ensure FRE is clear whilst running _init_fpu, since
+                * single precision FP instructions are used. If FRE
+                * was set then we'll just end up initialising all 32
+                * 64b registers.
+                */
+               write_c0_config5(config5 & ~MIPS_CONF5_FRE);
+               enable_fpu_hazard();
+
+               _init_fpu();
+
+               /* Restore FRE */
+               write_c0_config5(config5);
+               enable_fpu_hazard();
        } else
                fpu_emulator_init_fpu();
 
This page took 0.032036 seconds and 5 git commands to generate.