|
@@ -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();
|
|
|
|