]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/mips/include/asm/fpu.h
Merge tag 'mvebu-dt-4.12-2' of git://git.infradead.org/linux-mvebu into next/dt
[karo-tx-linux.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/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 */