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