Blame view
arch/mips/include/asm/fpu.h
4.7 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 |
/* * Copyright (C) 2002 MontaVista Software Inc. * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #ifndef _ASM_FPU_H #define _ASM_FPU_H |
1da177e4c
|
12 13 |
#include <linux/sched.h> #include <linux/thread_info.h> |
1977f0327
|
14 |
#include <linux/bitops.h> |
1da177e4c
|
15 16 17 18 |
#include <asm/mipsregs.h> #include <asm/cpu.h> #include <asm/cpu-features.h> |
e0cc3a42e
|
19 |
#include <asm/fpu_emulator.h> |
0b6249567
|
20 |
#include <asm/hazards.h> |
1da177e4c
|
21 22 |
#include <asm/processor.h> #include <asm/current.h> |
33c771ba5
|
23 |
#include <asm/msa.h> |
1da177e4c
|
24 |
|
f088fc84f
|
25 26 27 |
#ifdef CONFIG_MIPS_MT_FPAFF #include <asm/mips_mt.h> #endif |
1da177e4c
|
28 29 |
struct sigcontext; struct sigcontext32; |
1da177e4c
|
30 31 32 |
extern void _init_fpu(void); extern void _save_fp(struct task_struct *); extern void _restore_fp(struct task_struct *); |
597ce1723
|
33 34 |
/* * This enum specifies a mode in which we want the FPU to operate, for cores |
4227a2d4e
|
35 36 |
* which implement the Status.FR bit. Note that the bottom bit of the value * purposefully matches the desired value of the Status.FR bit. |
597ce1723
|
37 38 39 |
*/ enum fpu_mode { FPU_32BIT = 0, /* FR = 0 */ |
4227a2d4e
|
40 |
FPU_64BIT, /* FR = 1, FRE = 0 */ |
597ce1723
|
41 |
FPU_AS_IS, |
4227a2d4e
|
42 43 44 |
FPU_HYBRID, /* FR = 1, FRE = 1 */ #define FPU_FR_MASK 0x1 |
597ce1723
|
45 46 47 48 49 50 51 52 53 54 55 56 |
}; static inline int __enable_fpu(enum fpu_mode mode) { int fr; switch (mode) { case FPU_AS_IS: /* just enable the FPU in its current mode */ set_c0_status(ST0_CU1); enable_fpu_hazard(); return 0; |
4227a2d4e
|
57 58 59 60 61 |
case FPU_HYBRID: if (!cpu_has_fre) return SIGFPE; /* set FRE */ |
d33e6fe3c
|
62 |
set_c0_config5(MIPS_CONF5_FRE); |
4227a2d4e
|
63 |
goto fr_common; |
597ce1723
|
64 |
case FPU_64BIT: |
f5868f05d
|
65 |
#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT)) |
597ce1723
|
66 67 68 69 70 |
/* we only have a 32-bit FPU */ return SIGFPE; #endif /* fall through */ case FPU_32BIT: |
b0c34f615
|
71 72 |
if (cpu_has_fre) { /* clear FRE */ |
d33e6fe3c
|
73 |
clear_c0_config5(MIPS_CONF5_FRE); |
b0c34f615
|
74 |
} |
4227a2d4e
|
75 |
fr_common: |
597ce1723
|
76 |
/* set CU1 & change FR appropriately */ |
4227a2d4e
|
77 |
fr = (int)mode & FPU_FR_MASK; |
597ce1723
|
78 79 80 81 82 83 84 85 86 |
change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); enable_fpu_hazard(); /* check FR has the desired value */ return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE; default: BUG(); } |
97b8b16bf
|
87 88 |
return SIGFPE; |
597ce1723
|
89 |
} |
1da177e4c
|
90 91 92 93 |
#define __disable_fpu() \ do { \ clear_c0_status(ST0_CU1); \ |
703422879
|
94 |
disable_fpu_hazard(); \ |
1da177e4c
|
95 |
} while (0) |
1da177e4c
|
96 |
#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) |
1d74f6bc8
|
97 98 99 100 |
static inline int __is_fpu_owner(void) { return test_thread_flag(TIF_USEDFPU); } |
1da177e4c
|
101 102 |
static inline int is_fpu_owner(void) { |
1d74f6bc8
|
103 |
return cpu_has_fpu && __is_fpu_owner(); |
1da177e4c
|
104 |
} |
597ce1723
|
105 |
static inline int __own_fpu(void) |
1da177e4c
|
106 |
{ |
597ce1723
|
107 108 |
enum fpu_mode mode; int ret; |
4227a2d4e
|
109 110 111 112 |
if (test_thread_flag(TIF_HYBRID_FPREGS)) mode = FPU_HYBRID; else mode = !test_thread_flag(TIF_32BIT_FPREGS); |
597ce1723
|
113 114 115 |
ret = __enable_fpu(mode); if (ret) return ret; |
53dc80287
|
116 |
KSTK_STATUS(current) |= ST0_CU1; |
4227a2d4e
|
117 |
if (mode == FPU_64BIT || mode == FPU_HYBRID) |
597ce1723
|
118 119 120 |
KSTK_STATUS(current) |= ST0_FR; else /* mode == FPU_32BIT */ KSTK_STATUS(current) &= ~ST0_FR; |
53dc80287
|
121 |
set_thread_flag(TIF_USEDFPU); |
597ce1723
|
122 |
return 0; |
53dc80287
|
123 |
} |
597ce1723
|
124 |
static inline int own_fpu_inatomic(int restore) |
53dc80287
|
125 |
{ |
597ce1723
|
126 |
int ret = 0; |
53dc80287
|
127 |
if (cpu_has_fpu && !__is_fpu_owner()) { |
597ce1723
|
128 129 |
ret = __own_fpu(); if (restore && !ret) |
53dc80287
|
130 |
_restore_fp(current); |
1da177e4c
|
131 |
} |
597ce1723
|
132 |
return ret; |
faea62346
|
133 |
} |
597ce1723
|
134 |
static inline int own_fpu(int restore) |
faea62346
|
135 |
{ |
597ce1723
|
136 |
int ret; |
faea62346
|
137 |
preempt_disable(); |
597ce1723
|
138 |
ret = own_fpu_inatomic(restore); |
53dc80287
|
139 |
preempt_enable(); |
597ce1723
|
140 |
return ret; |
1da177e4c
|
141 |
} |
53dc80287
|
142 |
static inline void lose_fpu(int save) |
1da177e4c
|
143 |
{ |
53dc80287
|
144 |
preempt_disable(); |
33c771ba5
|
145 146 147 |
if (is_msa_enabled()) { if (save) { save_msa(current); |
842dfc11e
|
148 149 |
current->thread.fpu.fcr31 = read_32bit_cp1_register(CP1_STATUS); |
33c771ba5
|
150 151 152 153 |
} disable_msa(); clear_thread_flag(TIF_USEDMSA); } else if (is_fpu_owner()) { |
53dc80287
|
154 155 |
if (save) _save_fp(current); |
1da177e4c
|
156 157 |
__disable_fpu(); } |
33c771ba5
|
158 159 |
KSTK_STATUS(current) &= ~ST0_CU1; clear_thread_flag(TIF_USEDFPU); |
53dc80287
|
160 |
preempt_enable(); |
1da177e4c
|
161 |
} |
597ce1723
|
162 |
static inline int init_fpu(void) |
1da177e4c
|
163 |
{ |
597ce1723
|
164 |
int ret = 0; |
1da177e4c
|
165 |
if (cpu_has_fpu) { |
b0c34f615
|
166 |
unsigned int config5; |
597ce1723
|
167 |
ret = __own_fpu(); |
b0c34f615
|
168 169 |
if (ret) return ret; |
4227a2d4e
|
170 |
|
b0c34f615
|
171 |
if (!cpu_has_fre) { |
597ce1723
|
172 |
_init_fpu(); |
4227a2d4e
|
173 |
|
b0c34f615
|
174 |
return 0; |
4227a2d4e
|
175 |
} |
b0c34f615
|
176 |
|
b0c34f615
|
177 178 179 180 181 182 |
/* * Ensure FRE is clear whilst running _init_fpu, since * single precision FP instructions are used. If FRE * was set then we'll just end up initialising all 32 * 64b registers. */ |
d33e6fe3c
|
183 |
config5 = clear_c0_config5(MIPS_CONF5_FRE); |
b0c34f615
|
184 185 186 187 188 189 190 |
enable_fpu_hazard(); _init_fpu(); /* Restore FRE */ write_c0_config5(config5); enable_fpu_hazard(); |
e0cc3a42e
|
191 |
} else |
1da177e4c
|
192 |
fpu_emulator_init_fpu(); |
597ce1723
|
193 |
|
597ce1723
|
194 |
return ret; |
1da177e4c
|
195 196 197 198 199 200 201 202 203 204 205 206 207 |
} static inline void save_fp(struct task_struct *tsk) { if (cpu_has_fpu) _save_fp(tsk); } static inline void restore_fp(struct task_struct *tsk) { if (cpu_has_fpu) _restore_fp(tsk); } |
bbd426f54
|
208 |
static inline union fpureg *get_fpu_regs(struct task_struct *tsk) |
1da177e4c
|
209 |
{ |
e04582b7b
|
210 211 212 |
if (tsk == current) { preempt_disable(); if (is_fpu_owner()) |
1da177e4c
|
213 |
_save_fp(current); |
e04582b7b
|
214 |
preempt_enable(); |
1da177e4c
|
215 |
} |
eae89076e
|
216 |
return tsk->thread.fpu.fpr; |
1da177e4c
|
217 218 219 |
} #endif /* _ASM_FPU_H */ |