diff options
Diffstat (limited to 'arch/mips/include/asm/fpu.h')
| -rw-r--r-- | arch/mips/include/asm/fpu.h | 115 | 
1 files changed, 83 insertions, 32 deletions
diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h index d088e5db490..a939574f829 100644 --- a/arch/mips/include/asm/fpu.h +++ b/arch/mips/include/asm/fpu.h @@ -17,6 +17,7 @@  #include <asm/mipsregs.h>  #include <asm/cpu.h>  #include <asm/cpu-features.h> +#include <asm/fpu_emulator.h>  #include <asm/hazards.h>  #include <asm/processor.h>  #include <asm/current.h> @@ -28,16 +29,54 @@  struct sigcontext;  struct sigcontext32; -extern void fpu_emulator_init_fpu(void);  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_64BIT)) +		/* 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(); +	} + +	return SIGFPE; +}  #define __disable_fpu()							\  do {									\ @@ -45,19 +84,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 +96,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 +151,22 @@ 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(); -	} else { +		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) @@ -130,7 +181,7 @@ static inline void restore_fp(struct task_struct *tsk)  		_restore_fp(tsk);  } -static inline fpureg_t *get_fpu_regs(struct task_struct *tsk) +static inline union fpureg *get_fpu_regs(struct task_struct *tsk)  {  	if (tsk == current) {  		preempt_disable();  | 
