summaryrefslogtreecommitdiff
path: root/arch/mips/include/asm/fpu.h
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/include/asm/fpu.h')
-rw-r--r--arch/mips/include/asm/fpu.h104
1 files changed, 76 insertions, 28 deletions
diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h
index d088e5db4903..cfe092fc720d 100644
--- a/arch/mips/include/asm/fpu.h
+++ b/arch/mips/include/asm/fpu.h
@@ -33,11 +33,48 @@ extern void _init_fpu(void);
extern void _save_fp(struct task_struct *);
extern void _restore_fp(struct task_struct *);
-#define __enable_fpu() \
-do { \
- set_c0_status(ST0_CU1); \
- enable_fpu_hazard(); \
-} while (0)
+/*
+ * 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.
+ */
+enum fpu_mode {
+ FPU_32BIT = 0, /* FR = 0 */
+ FPU_64BIT, /* FR = 1 */
+ FPU_AS_IS,
+};
+
+static inline int __enable_fpu(enum fpu_mode mode)
+{
+ int fr;
+
+ switch (mode) {
+ case FPU_AS_IS:
+ /* just enable the FPU in its current mode */
+ set_c0_status(ST0_CU1);
+ enable_fpu_hazard();
+ return 0;
+
+ case FPU_64BIT:
+#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_MIPS64))
+ /* we only have a 32-bit FPU */
+ return SIGFPE;
+#endif
+ /* fall through */
+ case FPU_32BIT:
+ /* set CU1 & change FR appropriately */
+ fr = (int)mode;
+ change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
+ enable_fpu_hazard();
+
+ /* check FR has the desired value */
+ return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE;
+
+ default:
+ BUG();
+ }
+}
#define __disable_fpu() \
do { \
@@ -45,19 +82,6 @@ do { \
disable_fpu_hazard(); \
} while (0)
-#define enable_fpu() \
-do { \
- if (cpu_has_fpu) \
- __enable_fpu(); \
-} while (0)
-
-#define disable_fpu() \
-do { \
- if (cpu_has_fpu) \
- __disable_fpu(); \
-} while (0)
-
-
#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU)
static inline int __is_fpu_owner(void)
@@ -70,27 +94,46 @@ static inline int is_fpu_owner(void)
return cpu_has_fpu && __is_fpu_owner();
}
-static inline void __own_fpu(void)
+static inline int __own_fpu(void)
{
- __enable_fpu();
+ enum fpu_mode mode;
+ int ret;
+
+ mode = !test_thread_flag(TIF_32BIT_FPREGS);
+ ret = __enable_fpu(mode);
+ if (ret)
+ return ret;
+
KSTK_STATUS(current) |= ST0_CU1;
+ if (mode == FPU_64BIT)
+ KSTK_STATUS(current) |= ST0_FR;
+ else /* mode == FPU_32BIT */
+ KSTK_STATUS(current) &= ~ST0_FR;
+
set_thread_flag(TIF_USEDFPU);
+ return 0;
}
-static inline void own_fpu_inatomic(int restore)
+static inline int own_fpu_inatomic(int restore)
{
+ int ret = 0;
+
if (cpu_has_fpu && !__is_fpu_owner()) {
- __own_fpu();
- if (restore)
+ ret = __own_fpu();
+ if (restore && !ret)
_restore_fp(current);
}
+ return ret;
}
-static inline void own_fpu(int restore)
+static inline int own_fpu(int restore)
{
+ int ret;
+
preempt_disable();
- own_fpu_inatomic(restore);
+ ret = own_fpu_inatomic(restore);
preempt_enable();
+ return ret;
}
static inline void lose_fpu(int save)
@@ -106,16 +149,21 @@ static inline void lose_fpu(int save)
preempt_enable();
}
-static inline void init_fpu(void)
+static inline int init_fpu(void)
{
+ int ret = 0;
+
preempt_disable();
if (cpu_has_fpu) {
- __own_fpu();
- _init_fpu();
+ ret = __own_fpu();
+ if (!ret)
+ _init_fpu();
} else {
fpu_emulator_init_fpu();
}
+
preempt_enable();
+ return ret;
}
static inline void save_fp(struct task_struct *tsk)