1 /* 2 * Copyright (C) 2002 MontaVista Software Inc. 3 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 */ 10 #ifndef _ASM_FPU_H 11 #define _ASM_FPU_H 12 13 #include <linux/sched.h> 14 #include <linux/thread_info.h> 15 #include <linux/bitops.h> 16 17 #include <asm/mipsregs.h> 18 #include <asm/cpu.h> 19 #include <asm/cpu-features.h> 20 #include <asm/hazards.h> 21 #include <asm/processor.h> 22 #include <asm/current.h> 23 24 #ifdef CONFIG_MIPS_MT_FPAFF 25 #include <asm/mips_mt.h> 26 #endif 27 28 struct sigcontext; 29 struct sigcontext32; 30 31 extern void fpu_emulator_init_fpu(void); 32 extern void _init_fpu(void); 33 extern void _save_fp(struct task_struct *); 34 extern void _restore_fp(struct task_struct *); 35 36 /* 37 * This enum specifies a mode in which we want the FPU to operate, for cores 38 * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT 39 * purposefully have the values 0 & 1 respectively, so that an integer value 40 * of Status.FR can be trivially casted to the corresponding enum fpu_mode. 41 */ 42 enum fpu_mode { 43 FPU_32BIT = 0, /* FR = 0 */ 44 FPU_64BIT, /* FR = 1 */ 45 FPU_AS_IS, 46 }; 47 48 static inline int __enable_fpu(enum fpu_mode mode) 49 { 50 int fr; 51 52 switch (mode) { 53 case FPU_AS_IS: 54 /* just enable the FPU in its current mode */ 55 set_c0_status(ST0_CU1); 56 enable_fpu_hazard(); 57 return 0; 58 59 case FPU_64BIT: 60 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_MIPS64)) 61 /* we only have a 32-bit FPU */ 62 return SIGFPE; 63 #endif 64 /* fall through */ 65 case FPU_32BIT: 66 /* set CU1 & change FR appropriately */ 67 fr = (int)mode; 68 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 69 enable_fpu_hazard(); 70 71 /* check FR has the desired value */ 72 return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE; 73 74 default: 75 BUG(); 76 } 77 } 78 79 #define __disable_fpu() \ 80 do { \ 81 clear_c0_status(ST0_CU1); \ 82 disable_fpu_hazard(); \ 83 } while (0) 84 85 #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 86 87 static inline int __is_fpu_owner(void) 88 { 89 return test_thread_flag(TIF_USEDFPU); 90 } 91 92 static inline int is_fpu_owner(void) 93 { 94 return cpu_has_fpu && __is_fpu_owner(); 95 } 96 97 static inline int __own_fpu(void) 98 { 99 enum fpu_mode mode; 100 int ret; 101 102 mode = !test_thread_flag(TIF_32BIT_FPREGS); 103 ret = __enable_fpu(mode); 104 if (ret) 105 return ret; 106 107 KSTK_STATUS(current) |= ST0_CU1; 108 if (mode == FPU_64BIT) 109 KSTK_STATUS(current) |= ST0_FR; 110 else /* mode == FPU_32BIT */ 111 KSTK_STATUS(current) &= ~ST0_FR; 112 113 set_thread_flag(TIF_USEDFPU); 114 return 0; 115 } 116 117 static inline int own_fpu_inatomic(int restore) 118 { 119 int ret = 0; 120 121 if (cpu_has_fpu && !__is_fpu_owner()) { 122 ret = __own_fpu(); 123 if (restore && !ret) 124 _restore_fp(current); 125 } 126 return ret; 127 } 128 129 static inline int own_fpu(int restore) 130 { 131 int ret; 132 133 preempt_disable(); 134 ret = own_fpu_inatomic(restore); 135 preempt_enable(); 136 return ret; 137 } 138 139 static inline void lose_fpu(int save) 140 { 141 preempt_disable(); 142 if (is_fpu_owner()) { 143 if (save) 144 _save_fp(current); 145 KSTK_STATUS(current) &= ~ST0_CU1; 146 clear_thread_flag(TIF_USEDFPU); 147 __disable_fpu(); 148 } 149 preempt_enable(); 150 } 151 152 static inline int init_fpu(void) 153 { 154 int ret = 0; 155 156 preempt_disable(); 157 if (cpu_has_fpu) { 158 ret = __own_fpu(); 159 if (!ret) 160 _init_fpu(); 161 } else { 162 fpu_emulator_init_fpu(); 163 } 164 165 preempt_enable(); 166 return ret; 167 } 168 169 static inline void save_fp(struct task_struct *tsk) 170 { 171 if (cpu_has_fpu) 172 _save_fp(tsk); 173 } 174 175 static inline void restore_fp(struct task_struct *tsk) 176 { 177 if (cpu_has_fpu) 178 _restore_fp(tsk); 179 } 180 181 static inline fpureg_t *get_fpu_regs(struct task_struct *tsk) 182 { 183 if (tsk == current) { 184 preempt_disable(); 185 if (is_fpu_owner()) 186 _save_fp(current); 187 preempt_enable(); 188 } 189 190 return tsk->thread.fpu.fpr; 191 } 192 193 #endif /* _ASM_FPU_H */ 194