xref: /openbmc/linux/arch/mips/include/asm/fpu.h (revision 3d9b448b)
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 the bottom bit of the value
40  * purposefully matches the desired value of the Status.FR bit.
41  */
42 enum fpu_mode {
43 	FPU_32BIT = 0,		/* FR = 0 */
44 	FPU_64BIT,		/* FR = 1, FRE = 0 */
45 	FPU_AS_IS,
46 	FPU_HYBRID,		/* FR = 1, FRE = 1 */
47 
48 #define FPU_FR_MASK		0x1
49 };
50 
51 static inline int __enable_fpu(enum fpu_mode mode)
52 {
53 	int fr;
54 
55 	switch (mode) {
56 	case FPU_AS_IS:
57 		/* just enable the FPU in its current mode */
58 		set_c0_status(ST0_CU1);
59 		enable_fpu_hazard();
60 		return 0;
61 
62 	case FPU_HYBRID:
63 		if (!cpu_has_fre)
64 			return SIGFPE;
65 
66 		/* set FRE */
67 		set_c0_config5(MIPS_CONF5_FRE);
68 		goto fr_common;
69 
70 	case FPU_64BIT:
71 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS32_R6) \
72       || defined(CONFIG_64BIT))
73 		/* we only have a 32-bit FPU */
74 		return SIGFPE;
75 #endif
76 		/* fall through */
77 	case FPU_32BIT:
78 		if (cpu_has_fre) {
79 			/* clear FRE */
80 			clear_c0_config5(MIPS_CONF5_FRE);
81 		}
82 fr_common:
83 		/* set CU1 & change FR appropriately */
84 		fr = (int)mode & FPU_FR_MASK;
85 		change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
86 		enable_fpu_hazard();
87 
88 		/* check FR has the desired value */
89 		return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE;
90 
91 	default:
92 		BUG();
93 	}
94 
95 	return SIGFPE;
96 }
97 
98 #define __disable_fpu()							\
99 do {									\
100 	clear_c0_status(ST0_CU1);					\
101 	disable_fpu_hazard();						\
102 } while (0)
103 
104 #define clear_fpu_owner()	clear_thread_flag(TIF_USEDFPU)
105 
106 static inline int __is_fpu_owner(void)
107 {
108 	return test_thread_flag(TIF_USEDFPU);
109 }
110 
111 static inline int is_fpu_owner(void)
112 {
113 	return cpu_has_fpu && __is_fpu_owner();
114 }
115 
116 static inline int __own_fpu(void)
117 {
118 	enum fpu_mode mode;
119 	int ret;
120 
121 	if (test_thread_flag(TIF_HYBRID_FPREGS))
122 		mode = FPU_HYBRID;
123 	else
124 		mode = !test_thread_flag(TIF_32BIT_FPREGS);
125 
126 	ret = __enable_fpu(mode);
127 	if (ret)
128 		return ret;
129 
130 	KSTK_STATUS(current) |= ST0_CU1;
131 	if (mode == FPU_64BIT || mode == FPU_HYBRID)
132 		KSTK_STATUS(current) |= ST0_FR;
133 	else /* mode == FPU_32BIT */
134 		KSTK_STATUS(current) &= ~ST0_FR;
135 
136 	set_thread_flag(TIF_USEDFPU);
137 	return 0;
138 }
139 
140 static inline int own_fpu_inatomic(int restore)
141 {
142 	int ret = 0;
143 
144 	if (cpu_has_fpu && !__is_fpu_owner()) {
145 		ret = __own_fpu();
146 		if (restore && !ret)
147 			_restore_fp(current);
148 	}
149 	return ret;
150 }
151 
152 static inline int own_fpu(int restore)
153 {
154 	int ret;
155 
156 	preempt_disable();
157 	ret = own_fpu_inatomic(restore);
158 	preempt_enable();
159 	return ret;
160 }
161 
162 static inline void lose_fpu(int save)
163 {
164 	preempt_disable();
165 	if (is_msa_enabled()) {
166 		if (save) {
167 			save_msa(current);
168 			current->thread.fpu.fcr31 =
169 					read_32bit_cp1_register(CP1_STATUS);
170 		}
171 		disable_msa();
172 		clear_thread_flag(TIF_USEDMSA);
173 	} else if (is_fpu_owner()) {
174 		if (save)
175 			_save_fp(current);
176 		__disable_fpu();
177 	}
178 	KSTK_STATUS(current) &= ~ST0_CU1;
179 	clear_thread_flag(TIF_USEDFPU);
180 	preempt_enable();
181 }
182 
183 static inline int init_fpu(void)
184 {
185 	int ret = 0;
186 
187 	if (cpu_has_fpu) {
188 		unsigned int config5;
189 
190 		ret = __own_fpu();
191 		if (ret)
192 			return ret;
193 
194 		if (!cpu_has_fre) {
195 			_init_fpu();
196 
197 			return 0;
198 		}
199 
200 		/*
201 		 * Ensure FRE is clear whilst running _init_fpu, since
202 		 * single precision FP instructions are used. If FRE
203 		 * was set then we'll just end up initialising all 32
204 		 * 64b registers.
205 		 */
206 		config5 = clear_c0_config5(MIPS_CONF5_FRE);
207 		enable_fpu_hazard();
208 
209 		_init_fpu();
210 
211 		/* Restore FRE */
212 		write_c0_config5(config5);
213 		enable_fpu_hazard();
214 	} else
215 		fpu_emulator_init_fpu();
216 
217 	return ret;
218 }
219 
220 static inline void save_fp(struct task_struct *tsk)
221 {
222 	if (cpu_has_fpu)
223 		_save_fp(tsk);
224 }
225 
226 static inline void restore_fp(struct task_struct *tsk)
227 {
228 	if (cpu_has_fpu)
229 		_restore_fp(tsk);
230 }
231 
232 static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
233 {
234 	if (tsk == current) {
235 		preempt_disable();
236 		if (is_fpu_owner())
237 			_save_fp(current);
238 		preempt_enable();
239 	}
240 
241 	return tsk->thread.fpu.fpr;
242 }
243 
244 #endif /* _ASM_FPU_H */
245