fpu.h 5.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * 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

#include <linux/sched.h>
14
#include <linux/sched/task_stack.h>
A
Arnd Bergmann 已提交
15
#include <linux/ptrace.h>
L
Linus Torvalds 已提交
16
#include <linux/thread_info.h>
J
Jiri Slaby 已提交
17
#include <linux/bitops.h>
L
Linus Torvalds 已提交
18 19 20 21

#include <asm/mipsregs.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
22
#include <asm/fpu_emulator.h>
C
Chris Dearman 已提交
23
#include <asm/hazards.h>
L
Linus Torvalds 已提交
24 25
#include <asm/processor.h>
#include <asm/current.h>
P
Paul Burton 已提交
26
#include <asm/msa.h>
L
Linus Torvalds 已提交
27

R
Ralf Baechle 已提交
28 29 30 31
#ifdef CONFIG_MIPS_MT_FPAFF
#include <asm/mips_mt.h>
#endif

L
Linus Torvalds 已提交
32 33 34
struct sigcontext;
struct sigcontext32;

35
extern void _init_fpu(unsigned int);
L
Linus Torvalds 已提交
36 37 38
extern void _save_fp(struct task_struct *);
extern void _restore_fp(struct task_struct *);

39 40
/*
 * This enum specifies a mode in which we want the FPU to operate, for cores
P
Paul Burton 已提交
41 42
 * which implement the Status.FR bit. Note that the bottom bit of the value
 * purposefully matches the desired value of the Status.FR bit.
43 44 45
 */
enum fpu_mode {
	FPU_32BIT = 0,		/* FR = 0 */
P
Paul Burton 已提交
46
	FPU_64BIT,		/* FR = 1, FRE = 0 */
47
	FPU_AS_IS,
P
Paul Burton 已提交
48 49 50
	FPU_HYBRID,		/* FR = 1, FRE = 1 */

#define FPU_FR_MASK		0x1
51 52
};

53 54 55 56 57 58
#define __disable_fpu()							\
do {									\
	clear_c0_status(ST0_CU1);					\
	disable_fpu_hazard();						\
} while (0)

59 60 61 62 63 64 65 66 67 68 69
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;

P
Paul Burton 已提交
70 71 72 73 74
	case FPU_HYBRID:
		if (!cpu_has_fre)
			return SIGFPE;

		/* set FRE */
75
		set_c0_config5(MIPS_CONF5_FRE);
P
Paul Burton 已提交
76 77
		goto fr_common;

78
	case FPU_64BIT:
79
#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \
80
      || defined(CONFIG_64BIT))
81 82 83 84 85
		/* we only have a 32-bit FPU */
		return SIGFPE;
#endif
		/* fall through */
	case FPU_32BIT:
86 87
		if (cpu_has_fre) {
			/* clear FRE */
88
			clear_c0_config5(MIPS_CONF5_FRE);
89
		}
P
Paul Burton 已提交
90
fr_common:
91
		/* set CU1 & change FR appropriately */
P
Paul Burton 已提交
92
		fr = (int)mode & FPU_FR_MASK;
93 94 95 96
		change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
		enable_fpu_hazard();

		/* check FR has the desired value */
97 98 99 100 101 102
		if (!!(read_c0_status() & ST0_FR) == !!fr)
			return 0;

		/* unsupported FR value */
		__disable_fpu();
		return SIGFPE;
103 104 105 106

	default:
		BUG();
	}
107 108

	return SIGFPE;
109
}
L
Linus Torvalds 已提交
110 111 112

#define clear_fpu_owner()	clear_thread_flag(TIF_USEDFPU)

113 114 115 116 117
static inline int __is_fpu_owner(void)
{
	return test_thread_flag(TIF_USEDFPU);
}

L
Linus Torvalds 已提交
118 119
static inline int is_fpu_owner(void)
{
120
	return cpu_has_fpu && __is_fpu_owner();
L
Linus Torvalds 已提交
121 122
}

123
static inline int __own_fpu(void)
L
Linus Torvalds 已提交
124
{
125 126 127
	enum fpu_mode mode;
	int ret;

P
Paul Burton 已提交
128 129 130 131 132
	if (test_thread_flag(TIF_HYBRID_FPREGS))
		mode = FPU_HYBRID;
	else
		mode = !test_thread_flag(TIF_32BIT_FPREGS);

133 134 135 136
	ret = __enable_fpu(mode);
	if (ret)
		return ret;

137
	KSTK_STATUS(current) |= ST0_CU1;
P
Paul Burton 已提交
138
	if (mode == FPU_64BIT || mode == FPU_HYBRID)
139 140 141 142
		KSTK_STATUS(current) |= ST0_FR;
	else /* mode == FPU_32BIT */
		KSTK_STATUS(current) &= ~ST0_FR;

143
	set_thread_flag(TIF_USEDFPU);
144
	return 0;
145 146
}

147
static inline int own_fpu_inatomic(int restore)
148
{
149 150
	int ret = 0;

151
	if (cpu_has_fpu && !__is_fpu_owner()) {
152 153
		ret = __own_fpu();
		if (restore && !ret)
154
			_restore_fp(current);
L
Linus Torvalds 已提交
155
	}
156
	return ret;
157 158
}

159
static inline int own_fpu(int restore)
160
{
161 162
	int ret;

163
	preempt_disable();
164
	ret = own_fpu_inatomic(restore);
165
	preempt_enable();
166
	return ret;
L
Linus Torvalds 已提交
167 168
}

169
static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
L
Linus Torvalds 已提交
170
{
P
Paul Burton 已提交
171 172
	if (is_msa_enabled()) {
		if (save) {
173 174
			save_msa(tsk);
			tsk->thread.fpu.fcr31 =
175
					read_32bit_cp1_register(CP1_STATUS);
P
Paul Burton 已提交
176 177
		}
		disable_msa();
178
		clear_tsk_thread_flag(tsk, TIF_USEDMSA);
179
		__disable_fpu();
P
Paul Burton 已提交
180
	} else if (is_fpu_owner()) {
181
		if (save)
182
			_save_fp(tsk);
L
Linus Torvalds 已提交
183
		__disable_fpu();
184 185 186 187
	} else {
		/* FPU should not have been left enabled with no owner */
		WARN(read_c0_status() & ST0_CU1,
		     "Orphaned FPU left enabled");
L
Linus Torvalds 已提交
188
	}
189 190 191 192 193 194 195 196
	KSTK_STATUS(tsk) &= ~ST0_CU1;
	clear_tsk_thread_flag(tsk, TIF_USEDFPU);
}

static inline void lose_fpu(int save)
{
	preempt_disable();
	lose_fpu_inatomic(save, current);
197
	preempt_enable();
L
Linus Torvalds 已提交
198 199
}

200
static inline int init_fpu(void)
L
Linus Torvalds 已提交
201
{
202
	unsigned int fcr31 = current->thread.fpu.fcr31;
203 204
	int ret = 0;

L
Linus Torvalds 已提交
205
	if (cpu_has_fpu) {
206 207
		unsigned int config5;

208
		ret = __own_fpu();
209 210
		if (ret)
			return ret;
P
Paul Burton 已提交
211

212
		if (!cpu_has_fre) {
213
			_init_fpu(fcr31);
P
Paul Burton 已提交
214

215
			return 0;
P
Paul Burton 已提交
216
		}
217 218 219 220 221 222 223

		/*
		 * 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.
		 */
224
		config5 = clear_c0_config5(MIPS_CONF5_FRE);
225 226
		enable_fpu_hazard();

227
		_init_fpu(fcr31);
228 229 230 231

		/* Restore FRE */
		write_c0_config5(config5);
		enable_fpu_hazard();
232
	} else
L
Linus Torvalds 已提交
233
		fpu_emulator_init_fpu();
234 235

	return ret;
L
Linus Torvalds 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249
}

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);
}

P
Paul Burton 已提交
250
static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
L
Linus Torvalds 已提交
251
{
252 253 254
	if (tsk == current) {
		preempt_disable();
		if (is_fpu_owner())
L
Linus Torvalds 已提交
255
			_save_fp(current);
256
		preempt_enable();
L
Linus Torvalds 已提交
257 258
	}

259
	return tsk->thread.fpu.fpr;
L
Linus Torvalds 已提交
260 261 262
}

#endif /* _ASM_FPU_H */