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/fpu_emulator.h> 21 #include <asm/hazards.h> 22 #include <asm/processor.h> 23 #include <asm/current.h> 24 #include <asm/msa.h> 25 26 #ifdef CONFIG_MIPS_MT_FPAFF 27 #include <asm/mips_mt.h> 28 #endif 29 30 struct sigcontext; 31 struct sigcontext32; 32 33 extern void _init_fpu(void); 34 extern void _save_fp(struct task_struct *); 35 extern void _restore_fp(struct task_struct *); 36 37 /* 38 * This enum specifies a mode in which we want the FPU to operate, for cores 39 * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT 40 * purposefully have the values 0 & 1 respectively, so that an integer value 41 * of Status.FR can be trivially casted to the corresponding enum fpu_mode. 42 */ 43 enum fpu_mode { 44 FPU_32BIT = 0, /* FR = 0 */ 45 FPU_64BIT, /* FR = 1 */ 46 FPU_AS_IS, 47 }; 48 49 static inline int __enable_fpu(enum fpu_mode mode) 50 { 51 int fr; 52 53 switch (mode) { 54 case FPU_AS_IS: 55 /* just enable the FPU in its current mode */ 56 set_c0_status(ST0_CU1); 57 enable_fpu_hazard(); 58 return 0; 59 60 case FPU_64BIT: 61 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT)) 62 /* we only have a 32-bit FPU */ 63 return SIGFPE; 64 #endif 65 /* fall through */ 66 case FPU_32BIT: 67 /* set CU1 & change FR appropriately */ 68 fr = (int)mode; 69 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 70 enable_fpu_hazard(); 71 72 /* check FR has the desired value */ 73 return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE; 74 75 default: 76 BUG(); 77 } 78 79 return SIGFPE; 80 } 81 82 #define __disable_fpu() \ 83 do { \ 84 clear_c0_status(ST0_CU1); \ 85 disable_fpu_hazard(); \ 86 } while (0) 87 88 #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 89 90 static inline int __is_fpu_owner(void) 91 { 92 return test_thread_flag(TIF_USEDFPU); 93 } 94 95 static inline int is_fpu_owner(void) 96 { 97 return cpu_has_fpu && __is_fpu_owner(); 98 } 99 100 static inline int __own_fpu(void) 101 { 102 enum fpu_mode mode; 103 int ret; 104 105 mode = !test_thread_flag(TIF_32BIT_FPREGS); 106 ret = __enable_fpu(mode); 107 if (ret) 108 return ret; 109 110 KSTK_STATUS(current) |= ST0_CU1; 111 if (mode == FPU_64BIT) 112 KSTK_STATUS(current) |= ST0_FR; 113 else /* mode == FPU_32BIT */ 114 KSTK_STATUS(current) &= ~ST0_FR; 115 116 set_thread_flag(TIF_USEDFPU); 117 return 0; 118 } 119 120 static inline int own_fpu_inatomic(int restore) 121 { 122 int ret = 0; 123 124 if (cpu_has_fpu && !__is_fpu_owner()) { 125 ret = __own_fpu(); 126 if (restore && !ret) 127 _restore_fp(current); 128 } 129 return ret; 130 } 131 132 static inline int own_fpu(int restore) 133 { 134 int ret; 135 136 preempt_disable(); 137 ret = own_fpu_inatomic(restore); 138 preempt_enable(); 139 return ret; 140 } 141 142 static inline void lose_fpu(int save) 143 { 144 preempt_disable(); 145 if (is_msa_enabled()) { 146 if (save) { 147 save_msa(current); 148 asm volatile("cfc1 %0, $31" 149 : "=r"(current->thread.fpu.fcr31)); 150 } 151 disable_msa(); 152 clear_thread_flag(TIF_USEDMSA); 153 } else if (is_fpu_owner()) { 154 if (save) 155 _save_fp(current); 156 __disable_fpu(); 157 } 158 KSTK_STATUS(current) &= ~ST0_CU1; 159 clear_thread_flag(TIF_USEDFPU); 160 preempt_enable(); 161 } 162 163 static inline int init_fpu(void) 164 { 165 int ret = 0; 166 167 if (cpu_has_fpu) { 168 ret = __own_fpu(); 169 if (!ret) 170 _init_fpu(); 171 } else 172 fpu_emulator_init_fpu(); 173 174 return ret; 175 } 176 177 static inline void save_fp(struct task_struct *tsk) 178 { 179 if (cpu_has_fpu) 180 _save_fp(tsk); 181 } 182 183 static inline void restore_fp(struct task_struct *tsk) 184 { 185 if (cpu_has_fpu) 186 _restore_fp(tsk); 187 } 188 189 static inline union fpureg *get_fpu_regs(struct task_struct *tsk) 190 { 191 if (tsk == current) { 192 preempt_disable(); 193 if (is_fpu_owner()) 194 _save_fp(current); 195 preempt_enable(); 196 } 197 198 return tsk->thread.fpu.fpr; 199 } 200 201 #endif /* _ASM_FPU_H */ 202