Merge branch 'irq-irqdomain-arm-for-linus' of git://git.kernel.org/pub/scm/linux...
[cascardo/linux.git] / arch / mips / include / asm / fpu.h
index dd56241..994d219 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,11 @@ static inline int __enable_fpu(enum fpu_mode mode)
 #endif
                /* fall through */
        case FPU_32BIT:
+               /* 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 +115,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;
@@ -166,8 +183,24 @@ static inline int init_fpu(void)
 
        if (cpu_has_fpu) {
                ret = __own_fpu();
-               if (!ret)
+               if (!ret) {
+                       unsigned int 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();