xref: /openbmc/linux/arch/sh/kernel/cpu/sh4/fpu.c (revision 1da177e4)
1 /* $Id: fpu.c,v 1.4 2004/01/13 05:52:11 kkojima Exp $
2  *
3  * linux/arch/sh/kernel/fpu.c
4  *
5  * Save/restore floating point context for signal handlers.
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  *
11  * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
12  *
13  * FIXME! These routines can be optimized in big endian case.
14  */
15 
16 #include <linux/sched.h>
17 #include <linux/signal.h>
18 #include <asm/processor.h>
19 #include <asm/io.h>
20 
21 /* The PR (precision) bit in the FP Status Register must be clear when
22  * an frchg instruction is executed, otherwise the instruction is undefined.
23  * Executing frchg with PR set causes a trap on some SH4 implementations.
24  */
25 
26 #define FPSCR_RCHG 0x00000000
27 
28 
29 /*
30  * Save FPU registers onto task structure.
31  * Assume called with FPU enabled (SR.FD=0).
32  */
33 void
34 save_fpu(struct task_struct *tsk, struct pt_regs *regs)
35 {
36 	unsigned long dummy;
37 
38 	clear_tsk_thread_flag(tsk, TIF_USEDFPU);
39 	enable_fpu();
40 	asm volatile("sts.l	fpul, @-%0\n\t"
41 		     "sts.l	fpscr, @-%0\n\t"
42 		     "lds	%2, fpscr\n\t"
43 		     "frchg\n\t"
44 		     "fmov.s	fr15, @-%0\n\t"
45 		     "fmov.s	fr14, @-%0\n\t"
46 		     "fmov.s	fr13, @-%0\n\t"
47 		     "fmov.s	fr12, @-%0\n\t"
48 		     "fmov.s	fr11, @-%0\n\t"
49 		     "fmov.s	fr10, @-%0\n\t"
50 		     "fmov.s	fr9, @-%0\n\t"
51 		     "fmov.s	fr8, @-%0\n\t"
52 		     "fmov.s	fr7, @-%0\n\t"
53 		     "fmov.s	fr6, @-%0\n\t"
54 		     "fmov.s	fr5, @-%0\n\t"
55 		     "fmov.s	fr4, @-%0\n\t"
56 		     "fmov.s	fr3, @-%0\n\t"
57 		     "fmov.s	fr2, @-%0\n\t"
58 		     "fmov.s	fr1, @-%0\n\t"
59 		     "fmov.s	fr0, @-%0\n\t"
60 		     "frchg\n\t"
61 		     "fmov.s	fr15, @-%0\n\t"
62 		     "fmov.s	fr14, @-%0\n\t"
63 		     "fmov.s	fr13, @-%0\n\t"
64 		     "fmov.s	fr12, @-%0\n\t"
65 		     "fmov.s	fr11, @-%0\n\t"
66 		     "fmov.s	fr10, @-%0\n\t"
67 		     "fmov.s	fr9, @-%0\n\t"
68 		     "fmov.s	fr8, @-%0\n\t"
69 		     "fmov.s	fr7, @-%0\n\t"
70 		     "fmov.s	fr6, @-%0\n\t"
71 		     "fmov.s	fr5, @-%0\n\t"
72 		     "fmov.s	fr4, @-%0\n\t"
73 		     "fmov.s	fr3, @-%0\n\t"
74 		     "fmov.s	fr2, @-%0\n\t"
75 		     "fmov.s	fr1, @-%0\n\t"
76 		     "fmov.s	fr0, @-%0\n\t"
77 		     "lds	%3, fpscr\n\t"
78 		     : "=r" (dummy)
79 		     : "0" ((char *)(&tsk->thread.fpu.hard.status)),
80 		       "r" (FPSCR_RCHG),
81 		       "r" (FPSCR_INIT)
82 		     : "memory");
83 
84  	disable_fpu();
85  	release_fpu(regs);
86 }
87 
88 static void
89 restore_fpu(struct task_struct *tsk)
90 {
91 	unsigned long dummy;
92 
93  	enable_fpu();
94 	asm volatile("lds	%2, fpscr\n\t"
95 		     "fmov.s	@%0+, fr0\n\t"
96 		     "fmov.s	@%0+, fr1\n\t"
97 		     "fmov.s	@%0+, fr2\n\t"
98 		     "fmov.s	@%0+, fr3\n\t"
99 		     "fmov.s	@%0+, fr4\n\t"
100 		     "fmov.s	@%0+, fr5\n\t"
101 		     "fmov.s	@%0+, fr6\n\t"
102 		     "fmov.s	@%0+, fr7\n\t"
103 		     "fmov.s	@%0+, fr8\n\t"
104 		     "fmov.s	@%0+, fr9\n\t"
105 		     "fmov.s	@%0+, fr10\n\t"
106 		     "fmov.s	@%0+, fr11\n\t"
107 		     "fmov.s	@%0+, fr12\n\t"
108 		     "fmov.s	@%0+, fr13\n\t"
109 		     "fmov.s	@%0+, fr14\n\t"
110 		     "fmov.s	@%0+, fr15\n\t"
111 		     "frchg\n\t"
112 		     "fmov.s	@%0+, fr0\n\t"
113 		     "fmov.s	@%0+, fr1\n\t"
114 		     "fmov.s	@%0+, fr2\n\t"
115 		     "fmov.s	@%0+, fr3\n\t"
116 		     "fmov.s	@%0+, fr4\n\t"
117 		     "fmov.s	@%0+, fr5\n\t"
118 		     "fmov.s	@%0+, fr6\n\t"
119 		     "fmov.s	@%0+, fr7\n\t"
120 		     "fmov.s	@%0+, fr8\n\t"
121 		     "fmov.s	@%0+, fr9\n\t"
122 		     "fmov.s	@%0+, fr10\n\t"
123 		     "fmov.s	@%0+, fr11\n\t"
124 		     "fmov.s	@%0+, fr12\n\t"
125 		     "fmov.s	@%0+, fr13\n\t"
126 		     "fmov.s	@%0+, fr14\n\t"
127 		     "fmov.s	@%0+, fr15\n\t"
128 		     "frchg\n\t"
129 		     "lds.l	@%0+, fpscr\n\t"
130 		     "lds.l	@%0+, fpul\n\t"
131 		     : "=r" (dummy)
132 		     : "0" (&tsk->thread.fpu), "r" (FPSCR_RCHG)
133 		     : "memory");
134 	disable_fpu();
135 }
136 
137 /*
138  * Load the FPU with signalling NANS.  This bit pattern we're using
139  * has the property that no matter wether considered as single or as
140  * double precission represents signaling NANS.
141  */
142 
143 static void
144 fpu_init(void)
145 {
146 	enable_fpu();
147 	asm volatile("lds	%0, fpul\n\t"
148 		     "lds	%1, fpscr\n\t"
149 		     "fsts	fpul, fr0\n\t"
150 		     "fsts	fpul, fr1\n\t"
151 		     "fsts	fpul, fr2\n\t"
152 		     "fsts	fpul, fr3\n\t"
153 		     "fsts	fpul, fr4\n\t"
154 		     "fsts	fpul, fr5\n\t"
155 		     "fsts	fpul, fr6\n\t"
156 		     "fsts	fpul, fr7\n\t"
157 		     "fsts	fpul, fr8\n\t"
158 		     "fsts	fpul, fr9\n\t"
159 		     "fsts	fpul, fr10\n\t"
160 		     "fsts	fpul, fr11\n\t"
161 		     "fsts	fpul, fr12\n\t"
162 		     "fsts	fpul, fr13\n\t"
163 		     "fsts	fpul, fr14\n\t"
164 		     "fsts	fpul, fr15\n\t"
165 		     "frchg\n\t"
166 		     "fsts	fpul, fr0\n\t"
167 		     "fsts	fpul, fr1\n\t"
168 		     "fsts	fpul, fr2\n\t"
169 		     "fsts	fpul, fr3\n\t"
170 		     "fsts	fpul, fr4\n\t"
171 		     "fsts	fpul, fr5\n\t"
172 		     "fsts	fpul, fr6\n\t"
173 		     "fsts	fpul, fr7\n\t"
174 		     "fsts	fpul, fr8\n\t"
175 		     "fsts	fpul, fr9\n\t"
176 		     "fsts	fpul, fr10\n\t"
177 		     "fsts	fpul, fr11\n\t"
178 		     "fsts	fpul, fr12\n\t"
179 		     "fsts	fpul, fr13\n\t"
180 		     "fsts	fpul, fr14\n\t"
181 		     "fsts	fpul, fr15\n\t"
182 		     "frchg\n\t"
183 		     "lds	%2, fpscr\n\t"
184 		     : /* no output */
185 		     : "r" (0), "r" (FPSCR_RCHG), "r" (FPSCR_INIT));
186  	disable_fpu();
187 }
188 
189 /**
190  *	denormal_to_double - Given denormalized float number,
191  *	                     store double float
192  *
193  *	@fpu: Pointer to sh_fpu_hard structure
194  *	@n: Index to FP register
195  */
196 static void
197 denormal_to_double (struct sh_fpu_hard_struct *fpu, int n)
198 {
199 	unsigned long du, dl;
200 	unsigned long x = fpu->fpul;
201 	int exp = 1023 - 126;
202 
203 	if (x != 0 && (x & 0x7f800000) == 0) {
204 		du = (x & 0x80000000);
205 		while ((x & 0x00800000) == 0) {
206 			x <<= 1;
207 			exp--;
208 		}
209 		x &= 0x007fffff;
210 		du |= (exp << 20) | (x >> 3);
211 		dl = x << 29;
212 
213 		fpu->fp_regs[n] = du;
214 		fpu->fp_regs[n+1] = dl;
215 	}
216 }
217 
218 /**
219  *	ieee_fpe_handler - Handle denormalized number exception
220  *
221  *	@regs: Pointer to register structure
222  *
223  *	Returns 1 when it's handled (should not cause exception).
224  */
225 static int
226 ieee_fpe_handler (struct pt_regs *regs)
227 {
228 	unsigned short insn = *(unsigned short *) regs->pc;
229 	unsigned short finsn;
230 	unsigned long nextpc;
231 	int nib[4] = {
232 		(insn >> 12) & 0xf,
233 		(insn >> 8) & 0xf,
234 		(insn >> 4) & 0xf,
235 		insn & 0xf};
236 
237 	if (nib[0] == 0xb ||
238 	    (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */
239 		regs->pr = regs->pc + 4;
240 
241 	if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */
242 		nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3);
243 		finsn = *(unsigned short *) (regs->pc + 2);
244 	} else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */
245 		if (regs->sr & 1)
246 			nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
247 		else
248 			nextpc = regs->pc + 4;
249 		finsn = *(unsigned short *) (regs->pc + 2);
250 	} else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */
251 		if (regs->sr & 1)
252 			nextpc = regs->pc + 4;
253 		else
254 			nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
255 		finsn = *(unsigned short *) (regs->pc + 2);
256 	} else if (nib[0] == 0x4 && nib[3] == 0xb &&
257 		 (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */
258 		nextpc = regs->regs[nib[1]];
259 		finsn = *(unsigned short *) (regs->pc + 2);
260 	} else if (nib[0] == 0x0 && nib[3] == 0x3 &&
261 		 (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */
262 		nextpc = regs->pc + 4 + regs->regs[nib[1]];
263 		finsn = *(unsigned short *) (regs->pc + 2);
264 	} else if (insn == 0x000b) { /* rts */
265 		nextpc = regs->pr;
266 		finsn = *(unsigned short *) (regs->pc + 2);
267 	} else {
268 		nextpc = regs->pc + 2;
269 		finsn = insn;
270 	}
271 
272 	if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */
273 		struct task_struct *tsk = current;
274 
275 		save_fpu(tsk, regs);
276 		if ((tsk->thread.fpu.hard.fpscr & (1 << 17))) {
277 			/* FPU error */
278 			denormal_to_double (&tsk->thread.fpu.hard,
279 					    (finsn >> 8) & 0xf);
280 			tsk->thread.fpu.hard.fpscr &=
281 				~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
282 			grab_fpu(regs);
283 			restore_fpu(tsk);
284 			set_tsk_thread_flag(tsk, TIF_USEDFPU);
285 		} else {
286 			tsk->thread.trap_no = 11;
287 			tsk->thread.error_code = 0;
288 			force_sig(SIGFPE, tsk);
289 		}
290 
291 		regs->pc = nextpc;
292 		return 1;
293 	}
294 
295 	return 0;
296 }
297 
298 asmlinkage void
299 do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7,
300 	     struct pt_regs regs)
301 {
302 	struct task_struct *tsk = current;
303 
304 	if (ieee_fpe_handler (&regs))
305 		return;
306 
307 	regs.pc += 2;
308 	save_fpu(tsk, &regs);
309 	tsk->thread.trap_no = 11;
310 	tsk->thread.error_code = 0;
311 	force_sig(SIGFPE, tsk);
312 }
313 
314 asmlinkage void
315 do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6,
316 		     unsigned long r7, struct pt_regs regs)
317 {
318 	struct task_struct *tsk = current;
319 
320 	grab_fpu(&regs);
321 	if (!user_mode(&regs)) {
322 		printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
323 		return;
324 	}
325 
326 	if (used_math()) {
327 		/* Using the FPU again.  */
328 		restore_fpu(tsk);
329 	} else	{
330 		/* First time FPU user.  */
331 		fpu_init();
332 		set_used_math();
333 	}
334 	set_tsk_thread_flag(tsk, TIF_USEDFPU);
335 }
336