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_64BIT)) 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 return SIGFPE; 79 } 80 81 #define __disable_fpu() \ 82 do { \ 83 clear_c0_status(ST0_CU1); \ 84 disable_fpu_hazard(); \ 85 } while (0) 86 87 #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 88 89 static inline int __is_fpu_owner(void) 90 { 91 return test_thread_flag(TIF_USEDFPU); 92 } 93 94 static inline int is_fpu_owner(void) 95 { 96 return cpu_has_fpu && __is_fpu_owner(); 97 } 98 99 static inline int __own_fpu(void) 100 { 101 enum fpu_mode mode; 102 int ret; 103 104 mode = !test_thread_flag(TIF_32BIT_FPREGS); 105 ret = __enable_fpu(mode); 106 if (ret) 107 return ret; 108 109 KSTK_STATUS(current) |= ST0_CU1; 110 if (mode == FPU_64BIT) 111 KSTK_STATUS(current) |= ST0_FR; 112 else /* mode == FPU_32BIT */ 113 KSTK_STATUS(current) &= ~ST0_FR; 114 115 set_thread_flag(TIF_USEDFPU); 116 return 0; 117 } 118 119 static inline int own_fpu_inatomic(int restore) 120 { 121 int ret = 0; 122 123 if (cpu_has_fpu && !__is_fpu_owner()) { 124 ret = __own_fpu(); 125 if (restore && !ret) 126 _restore_fp(current); 127 } 128 return ret; 129 } 130 131 static inline int own_fpu(int restore) 132 { 133 int ret; 134 135 preempt_disable(); 136 ret = own_fpu_inatomic(restore); 137 preempt_enable(); 138 return ret; 139 } 140 141 static inline void lose_fpu(int save) 142 { 143 preempt_disable(); 144 if (is_fpu_owner()) { 145 if (save) 146 _save_fp(current); 147 KSTK_STATUS(current) &= ~ST0_CU1; 148 clear_thread_flag(TIF_USEDFPU); 149 __disable_fpu(); 150 } 151 preempt_enable(); 152 } 153 154 static inline int init_fpu(void) 155 { 156 int ret = 0; 157 158 preempt_disable(); 159 if (cpu_has_fpu) { 160 ret = __own_fpu(); 161 if (!ret) 162 _init_fpu(); 163 } else { 164 fpu_emulator_init_fpu(); 165 } 166 167 preempt_enable(); 168 return ret; 169 } 170 171 static inline void save_fp(struct task_struct *tsk) 172 { 173 if (cpu_has_fpu) 174 _save_fp(tsk); 175 } 176 177 static inline void restore_fp(struct task_struct *tsk) 178 { 179 if (cpu_has_fpu) 180 _restore_fp(tsk); 181 } 182 183 static inline union fpureg *get_fpu_regs(struct task_struct *tsk) 184 { 185 if (tsk == current) { 186 preempt_disable(); 187 if (is_fpu_owner()) 188 _save_fp(current); 189 preempt_enable(); 190 } 191 192 return tsk->thread.fpu.fpr; 193 } 194 195 #endif /* _ASM_FPU_H */ 196