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/sched/task_stack.h> 15 #include <linux/ptrace.h> 16 #include <linux/thread_info.h> 17 #include <linux/bitops.h> 18 19 #include <asm/mipsregs.h> 20 #include <asm/cpu.h> 21 #include <asm/cpu-features.h> 22 #include <asm/fpu_emulator.h> 23 #include <asm/hazards.h> 24 #include <asm/processor.h> 25 #include <asm/current.h> 26 #include <asm/msa.h> 27 28 #ifdef CONFIG_MIPS_MT_FPAFF 29 #include <asm/mips_mt.h> 30 #endif 31 32 struct sigcontext; 33 struct sigcontext32; 34 35 extern void _init_fpu(unsigned int); 36 extern void _save_fp(struct task_struct *); 37 extern void _restore_fp(struct task_struct *); 38 39 /* 40 * This enum specifies a mode in which we want the FPU to operate, for cores 41 * which implement the Status.FR bit. Note that the bottom bit of the value 42 * purposefully matches the desired value of the Status.FR bit. 43 */ 44 enum fpu_mode { 45 FPU_32BIT = 0, /* FR = 0 */ 46 FPU_64BIT, /* FR = 1, FRE = 0 */ 47 FPU_AS_IS, 48 FPU_HYBRID, /* FR = 1, FRE = 1 */ 49 50 #define FPU_FR_MASK 0x1 51 }; 52 53 #define __disable_fpu() \ 54 do { \ 55 clear_c0_status(ST0_CU1); \ 56 disable_fpu_hazard(); \ 57 } while (0) 58 59 static inline int __enable_fpu(enum fpu_mode mode) 60 { 61 int fr; 62 63 switch (mode) { 64 case FPU_AS_IS: 65 /* just enable the FPU in its current mode */ 66 set_c0_status(ST0_CU1); 67 enable_fpu_hazard(); 68 return 0; 69 70 case FPU_HYBRID: 71 if (!cpu_has_fre) 72 return SIGFPE; 73 74 /* set FRE */ 75 set_c0_config5(MIPS_CONF5_FRE); 76 goto fr_common; 77 78 case FPU_64BIT: 79 #if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \ 80 || defined(CONFIG_64BIT)) 81 /* we only have a 32-bit FPU */ 82 return SIGFPE; 83 #endif 84 /* fall through */ 85 case FPU_32BIT: 86 if (cpu_has_fre) { 87 /* clear FRE */ 88 clear_c0_config5(MIPS_CONF5_FRE); 89 } 90 fr_common: 91 /* set CU1 & change FR appropriately */ 92 fr = (int)mode & FPU_FR_MASK; 93 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 94 enable_fpu_hazard(); 95 96 /* check FR has the desired value */ 97 if (!!(read_c0_status() & ST0_FR) == !!fr) 98 return 0; 99 100 /* unsupported FR value */ 101 __disable_fpu(); 102 return SIGFPE; 103 104 default: 105 BUG(); 106 } 107 108 return SIGFPE; 109 } 110 111 #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 112 113 static inline int __is_fpu_owner(void) 114 { 115 return test_thread_flag(TIF_USEDFPU); 116 } 117 118 static inline int is_fpu_owner(void) 119 { 120 return cpu_has_fpu && __is_fpu_owner(); 121 } 122 123 static inline int __own_fpu(void) 124 { 125 enum fpu_mode mode; 126 int ret; 127 128 if (test_thread_flag(TIF_HYBRID_FPREGS)) 129 mode = FPU_HYBRID; 130 else 131 mode = !test_thread_flag(TIF_32BIT_FPREGS); 132 133 ret = __enable_fpu(mode); 134 if (ret) 135 return ret; 136 137 KSTK_STATUS(current) |= ST0_CU1; 138 if (mode == FPU_64BIT || mode == FPU_HYBRID) 139 KSTK_STATUS(current) |= ST0_FR; 140 else /* mode == FPU_32BIT */ 141 KSTK_STATUS(current) &= ~ST0_FR; 142 143 set_thread_flag(TIF_USEDFPU); 144 return 0; 145 } 146 147 static inline int own_fpu_inatomic(int restore) 148 { 149 int ret = 0; 150 151 if (cpu_has_fpu && !__is_fpu_owner()) { 152 ret = __own_fpu(); 153 if (restore && !ret) 154 _restore_fp(current); 155 } 156 return ret; 157 } 158 159 static inline int own_fpu(int restore) 160 { 161 int ret; 162 163 preempt_disable(); 164 ret = own_fpu_inatomic(restore); 165 preempt_enable(); 166 return ret; 167 } 168 169 static inline void lose_fpu_inatomic(int save, struct task_struct *tsk) 170 { 171 if (is_msa_enabled()) { 172 if (save) { 173 save_msa(tsk); 174 tsk->thread.fpu.fcr31 = 175 read_32bit_cp1_register(CP1_STATUS); 176 } 177 disable_msa(); 178 clear_tsk_thread_flag(tsk, TIF_USEDMSA); 179 __disable_fpu(); 180 } else if (is_fpu_owner()) { 181 if (save) 182 _save_fp(tsk); 183 __disable_fpu(); 184 } else { 185 /* FPU should not have been left enabled with no owner */ 186 WARN(read_c0_status() & ST0_CU1, 187 "Orphaned FPU left enabled"); 188 } 189 KSTK_STATUS(tsk) &= ~ST0_CU1; 190 clear_tsk_thread_flag(tsk, TIF_USEDFPU); 191 } 192 193 static inline void lose_fpu(int save) 194 { 195 preempt_disable(); 196 lose_fpu_inatomic(save, current); 197 preempt_enable(); 198 } 199 200 static inline int init_fpu(void) 201 { 202 unsigned int fcr31 = current->thread.fpu.fcr31; 203 int ret = 0; 204 205 if (cpu_has_fpu) { 206 unsigned int config5; 207 208 ret = __own_fpu(); 209 if (ret) 210 return ret; 211 212 if (!cpu_has_fre) { 213 _init_fpu(fcr31); 214 215 return 0; 216 } 217 218 /* 219 * Ensure FRE is clear whilst running _init_fpu, since 220 * single precision FP instructions are used. If FRE 221 * was set then we'll just end up initialising all 32 222 * 64b registers. 223 */ 224 config5 = clear_c0_config5(MIPS_CONF5_FRE); 225 enable_fpu_hazard(); 226 227 _init_fpu(fcr31); 228 229 /* Restore FRE */ 230 write_c0_config5(config5); 231 enable_fpu_hazard(); 232 } else 233 fpu_emulator_init_fpu(); 234 235 return ret; 236 } 237 238 static inline void save_fp(struct task_struct *tsk) 239 { 240 if (cpu_has_fpu) 241 _save_fp(tsk); 242 } 243 244 static inline void restore_fp(struct task_struct *tsk) 245 { 246 if (cpu_has_fpu) 247 _restore_fp(tsk); 248 } 249 250 static inline union fpureg *get_fpu_regs(struct task_struct *tsk) 251 { 252 if (tsk == current) { 253 preempt_disable(); 254 if (is_fpu_owner()) 255 _save_fp(current); 256 preempt_enable(); 257 } 258 259 return tsk->thread.fpu.fpr; 260 } 261 262 #endif /* _ASM_FPU_H */ 263