2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2002 MontaVista Software Inc .
* Author : Jun Sun , jsun @ mvista . com or jsun @ junsun . net
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# ifndef _ASM_FPU_H
# define _ASM_FPU_H
# include <linux/sched.h>
# include <linux/thread_info.h>
2007-10-18 23:40:25 -07:00
# include <linux/bitops.h>
2005-04-16 15:20:36 -07:00
# include <asm/mipsregs.h>
# include <asm/cpu.h>
# include <asm/cpu-features.h>
2007-05-08 16:09:13 +01:00
# include <asm/hazards.h>
2005-04-16 15:20:36 -07:00
# include <asm/processor.h>
# include <asm/current.h>
2006-04-05 09:45:47 +01:00
# ifdef CONFIG_MIPS_MT_FPAFF
# include <asm/mips_mt.h>
# endif
2005-04-16 15:20:36 -07:00
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 * ) ;
2013-11-22 13:12:07 +00:00
/*
* 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 ( ) ;
}
}
2005-04-16 15:20:36 -07:00
# define __disable_fpu() \
do { \
clear_c0_status ( ST0_CU1 ) ; \
2013-01-22 12:59:30 +01:00
disable_fpu_hazard ( ) ; \
2005-04-16 15:20:36 -07:00
} while ( 0 )
# define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU)
2005-05-09 13:16:07 +00:00
static inline int __is_fpu_owner ( void )
{
return test_thread_flag ( TIF_USEDFPU ) ;
}
2005-04-16 15:20:36 -07:00
static inline int is_fpu_owner ( void )
{
2005-05-09 13:16:07 +00:00
return cpu_has_fpu & & __is_fpu_owner ( ) ;
2005-04-16 15:20:36 -07:00
}
2013-11-22 13:12:07 +00:00
static inline int __own_fpu ( void )
2005-04-16 15:20:36 -07:00
{
2013-11-22 13:12:07 +00:00
enum fpu_mode mode ;
int ret ;
mode = ! test_thread_flag ( TIF_32BIT_FPREGS ) ;
ret = __enable_fpu ( mode ) ;
if ( ret )
return ret ;
2007-03-10 01:07:45 +09:00
KSTK_STATUS ( current ) | = ST0_CU1 ;
2013-11-22 13:12:07 +00:00
if ( mode = = FPU_64BIT )
KSTK_STATUS ( current ) | = ST0_FR ;
else /* mode == FPU_32BIT */
KSTK_STATUS ( current ) & = ~ ST0_FR ;
2007-03-10 01:07:45 +09:00
set_thread_flag ( TIF_USEDFPU ) ;
2013-11-22 13:12:07 +00:00
return 0 ;
2007-03-10 01:07:45 +09:00
}
2013-11-22 13:12:07 +00:00
static inline int own_fpu_inatomic ( int restore )
2007-03-10 01:07:45 +09:00
{
2013-11-22 13:12:07 +00:00
int ret = 0 ;
2007-03-10 01:07:45 +09:00
if ( cpu_has_fpu & & ! __is_fpu_owner ( ) ) {
2013-11-22 13:12:07 +00:00
ret = __own_fpu ( ) ;
if ( restore & & ! ret )
2007-03-10 01:07:45 +09:00
_restore_fp ( current ) ;
2005-04-16 15:20:36 -07:00
}
2013-11-22 13:12:07 +00:00
return ret ;
2007-04-16 23:19:44 +09:00
}
2013-11-22 13:12:07 +00:00
static inline int own_fpu ( int restore )
2007-04-16 23:19:44 +09:00
{
2013-11-22 13:12:07 +00:00
int ret ;
2007-04-16 23:19:44 +09:00
preempt_disable ( ) ;
2013-11-22 13:12:07 +00:00
ret = own_fpu_inatomic ( restore ) ;
2007-03-10 01:07:45 +09:00
preempt_enable ( ) ;
2013-11-22 13:12:07 +00:00
return ret ;
2005-04-16 15:20:36 -07:00
}
2007-03-10 01:07:45 +09:00
static inline void lose_fpu ( int save )
2005-04-16 15:20:36 -07:00
{
2007-03-10 01:07:45 +09:00
preempt_disable ( ) ;
if ( is_fpu_owner ( ) ) {
if ( save )
_save_fp ( current ) ;
2005-04-16 15:20:36 -07:00
KSTK_STATUS ( current ) & = ~ ST0_CU1 ;
2005-09-03 15:56:17 -07:00
clear_thread_flag ( TIF_USEDFPU ) ;
2005-04-16 15:20:36 -07:00
__disable_fpu ( ) ;
}
2007-03-10 01:07:45 +09:00
preempt_enable ( ) ;
2005-04-16 15:20:36 -07:00
}
2013-11-22 13:12:07 +00:00
static inline int init_fpu ( void )
2005-04-16 15:20:36 -07:00
{
2013-11-22 13:12:07 +00:00
int ret = 0 ;
2007-03-10 01:07:45 +09:00
preempt_disable ( ) ;
2005-04-16 15:20:36 -07:00
if ( cpu_has_fpu ) {
2013-11-22 13:12:07 +00:00
ret = __own_fpu ( ) ;
if ( ! ret )
_init_fpu ( ) ;
2005-04-16 15:20:36 -07:00
} else {
fpu_emulator_init_fpu ( ) ;
}
2013-11-22 13:12:07 +00:00
2007-03-10 01:07:45 +09:00
preempt_enable ( ) ;
2013-11-22 13:12:07 +00:00
return ret ;
2005-04-16 15:20:36 -07:00
}
static inline void save_fp ( struct task_struct * tsk )
{
if ( cpu_has_fpu )
_save_fp ( tsk ) ;
}
static inline void restore_fp ( struct task_struct * tsk )
{
if ( cpu_has_fpu )
_restore_fp ( tsk ) ;
}
static inline fpureg_t * get_fpu_regs ( struct task_struct * tsk )
{
2006-10-09 00:10:01 +09:00
if ( tsk = = current ) {
preempt_disable ( ) ;
if ( is_fpu_owner ( ) )
2005-04-16 15:20:36 -07:00
_save_fp ( current ) ;
2006-10-09 00:10:01 +09:00
preempt_enable ( ) ;
2005-04-16 15:20:36 -07:00
}
2006-05-16 01:26:03 +09:00
return tsk - > thread . fpu . fpr ;
2005-04-16 15:20:36 -07:00
}
# endif /* _ASM_FPU_H */