xref: /openbmc/linux/arch/sh/kernel/cpu/sh2a/fpu.c (revision 3cf5d076)
1234a0538SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
274d99a5eSPaul Mundt /*
374d99a5eSPaul Mundt  * Save/restore floating point context for signal handlers.
474d99a5eSPaul Mundt  *
574d99a5eSPaul Mundt  * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
674d99a5eSPaul Mundt  *
774d99a5eSPaul Mundt  * FIXME! These routines can be optimized in big endian case.
874d99a5eSPaul Mundt  */
93f07c014SIngo Molnar #include <linux/sched/signal.h>
1074d99a5eSPaul Mundt #include <linux/signal.h>
1174d99a5eSPaul Mundt #include <asm/processor.h>
1274d99a5eSPaul Mundt #include <asm/io.h>
139bbafce2SPaul Mundt #include <asm/fpu.h>
14f03c4866SPaul Mundt #include <asm/traps.h>
1574d99a5eSPaul Mundt 
1674d99a5eSPaul Mundt /* The PR (precision) bit in the FP Status Register must be clear when
1774d99a5eSPaul Mundt  * an frchg instruction is executed, otherwise the instruction is undefined.
1874d99a5eSPaul Mundt  * Executing frchg with PR set causes a trap on some SH4 implementations.
1974d99a5eSPaul Mundt  */
2074d99a5eSPaul Mundt 
2174d99a5eSPaul Mundt #define FPSCR_RCHG 0x00000000
2274d99a5eSPaul Mundt 
2374d99a5eSPaul Mundt 
2474d99a5eSPaul Mundt /*
2574d99a5eSPaul Mundt  * Save FPU registers onto task structure.
2674d99a5eSPaul Mundt  */
save_fpu(struct task_struct * tsk)270ea820cfSPaul Mundt void save_fpu(struct task_struct *tsk)
2874d99a5eSPaul Mundt {
2974d99a5eSPaul Mundt 	unsigned long dummy;
3074d99a5eSPaul Mundt 
3174d99a5eSPaul Mundt 	enable_fpu();
3274d99a5eSPaul Mundt 	asm volatile("sts.l	fpul, @-%0\n\t"
3374d99a5eSPaul Mundt 		     "sts.l	fpscr, @-%0\n\t"
3474d99a5eSPaul Mundt 		     "fmov.s	fr15, @-%0\n\t"
3574d99a5eSPaul Mundt 		     "fmov.s	fr14, @-%0\n\t"
3674d99a5eSPaul Mundt 		     "fmov.s	fr13, @-%0\n\t"
3774d99a5eSPaul Mundt 		     "fmov.s	fr12, @-%0\n\t"
3874d99a5eSPaul Mundt 		     "fmov.s	fr11, @-%0\n\t"
3974d99a5eSPaul Mundt 		     "fmov.s	fr10, @-%0\n\t"
4074d99a5eSPaul Mundt 		     "fmov.s	fr9, @-%0\n\t"
4174d99a5eSPaul Mundt 		     "fmov.s	fr8, @-%0\n\t"
4274d99a5eSPaul Mundt 		     "fmov.s	fr7, @-%0\n\t"
4374d99a5eSPaul Mundt 		     "fmov.s	fr6, @-%0\n\t"
4474d99a5eSPaul Mundt 		     "fmov.s	fr5, @-%0\n\t"
4574d99a5eSPaul Mundt 		     "fmov.s	fr4, @-%0\n\t"
4674d99a5eSPaul Mundt 		     "fmov.s	fr3, @-%0\n\t"
4774d99a5eSPaul Mundt 		     "fmov.s	fr2, @-%0\n\t"
4874d99a5eSPaul Mundt 		     "fmov.s	fr1, @-%0\n\t"
4974d99a5eSPaul Mundt 		     "fmov.s	fr0, @-%0\n\t"
5074d99a5eSPaul Mundt 		     "lds	%3, fpscr\n\t"
5174d99a5eSPaul Mundt 		     : "=r" (dummy)
520ea820cfSPaul Mundt 		     : "0" ((char *)(&tsk->thread.xstate->hardfpu.status)),
5374d99a5eSPaul Mundt 		       "r" (FPSCR_RCHG),
5474d99a5eSPaul Mundt 		       "r" (FPSCR_INIT)
5574d99a5eSPaul Mundt 		     : "memory");
5674d99a5eSPaul Mundt 
5774d99a5eSPaul Mundt 	disable_fpu();
5874d99a5eSPaul Mundt }
5974d99a5eSPaul Mundt 
restore_fpu(struct task_struct * tsk)600ea820cfSPaul Mundt void restore_fpu(struct task_struct *tsk)
6174d99a5eSPaul Mundt {
6274d99a5eSPaul Mundt 	unsigned long dummy;
6374d99a5eSPaul Mundt 
6474d99a5eSPaul Mundt 	enable_fpu();
6574d99a5eSPaul Mundt 	asm volatile("fmov.s	@%0+, fr0\n\t"
6674d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr1\n\t"
6774d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr2\n\t"
6874d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr3\n\t"
6974d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr4\n\t"
7074d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr5\n\t"
7174d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr6\n\t"
7274d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr7\n\t"
7374d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr8\n\t"
7474d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr9\n\t"
7574d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr10\n\t"
7674d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr11\n\t"
7774d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr12\n\t"
7874d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr13\n\t"
7974d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr14\n\t"
8074d99a5eSPaul Mundt 		     "fmov.s	@%0+, fr15\n\t"
8174d99a5eSPaul Mundt 		     "lds.l	@%0+, fpscr\n\t"
8274d99a5eSPaul Mundt 		     "lds.l	@%0+, fpul\n\t"
8374d99a5eSPaul Mundt 		     : "=r" (dummy)
840ea820cfSPaul Mundt 		     : "0" (tsk->thread.xstate), "r" (FPSCR_RCHG)
8574d99a5eSPaul Mundt 		     : "memory");
8674d99a5eSPaul Mundt 	disable_fpu();
8774d99a5eSPaul Mundt }
8874d99a5eSPaul Mundt 
8974d99a5eSPaul Mundt /*
9074d99a5eSPaul Mundt  *	Emulate arithmetic ops on denormalized number for some FPU insns.
9174d99a5eSPaul Mundt  */
9274d99a5eSPaul Mundt 
9374d99a5eSPaul Mundt /* denormalized float * float */
denormal_mulf(int hx,int hy)9474d99a5eSPaul Mundt static int denormal_mulf(int hx, int hy)
9574d99a5eSPaul Mundt {
9674d99a5eSPaul Mundt 	unsigned int ix, iy;
9774d99a5eSPaul Mundt 	unsigned long long m, n;
9874d99a5eSPaul Mundt 	int exp, w;
9974d99a5eSPaul Mundt 
10074d99a5eSPaul Mundt 	ix = hx & 0x7fffffff;
10174d99a5eSPaul Mundt 	iy = hy & 0x7fffffff;
10274d99a5eSPaul Mundt 	if (iy < 0x00800000 || ix == 0)
10374d99a5eSPaul Mundt 		return ((hx ^ hy) & 0x80000000);
10474d99a5eSPaul Mundt 
10574d99a5eSPaul Mundt 	exp = (iy & 0x7f800000) >> 23;
10674d99a5eSPaul Mundt 	ix &= 0x007fffff;
10774d99a5eSPaul Mundt 	iy = (iy & 0x007fffff) | 0x00800000;
10874d99a5eSPaul Mundt 	m = (unsigned long long)ix * iy;
10974d99a5eSPaul Mundt 	n = m;
11074d99a5eSPaul Mundt 	w = -1;
11174d99a5eSPaul Mundt 	while (n) { n >>= 1; w++; }
11274d99a5eSPaul Mundt 
11374d99a5eSPaul Mundt 	/* FIXME: use guard bits */
11474d99a5eSPaul Mundt 	exp += w - 126 - 46;
11574d99a5eSPaul Mundt 	if (exp > 0)
11674d99a5eSPaul Mundt 		ix = ((int) (m >> (w - 23)) & 0x007fffff) | (exp << 23);
11774d99a5eSPaul Mundt 	else if (exp + 22 >= 0)
11874d99a5eSPaul Mundt 		ix = (int) (m >> (w - 22 - exp)) & 0x007fffff;
11974d99a5eSPaul Mundt 	else
12074d99a5eSPaul Mundt 		ix = 0;
12174d99a5eSPaul Mundt 
12274d99a5eSPaul Mundt 	ix |= (hx ^ hy) & 0x80000000;
12374d99a5eSPaul Mundt 	return ix;
12474d99a5eSPaul Mundt }
12574d99a5eSPaul Mundt 
12674d99a5eSPaul Mundt /* denormalized double * double */
mult64(unsigned long long x,unsigned long long y,unsigned long long * highp,unsigned long long * lowp)12774d99a5eSPaul Mundt static void mult64(unsigned long long x, unsigned long long y,
12874d99a5eSPaul Mundt 		unsigned long long *highp, unsigned long long *lowp)
12974d99a5eSPaul Mundt {
13074d99a5eSPaul Mundt 	unsigned long long sub0, sub1, sub2, sub3;
13174d99a5eSPaul Mundt 	unsigned long long high, low;
13274d99a5eSPaul Mundt 
13374d99a5eSPaul Mundt 	sub0 = (x >> 32) * (unsigned long) (y >> 32);
13474d99a5eSPaul Mundt 	sub1 = (x & 0xffffffffLL) * (unsigned long) (y >> 32);
13574d99a5eSPaul Mundt 	sub2 = (x >> 32) * (unsigned long) (y & 0xffffffffLL);
13674d99a5eSPaul Mundt 	sub3 = (x & 0xffffffffLL) * (unsigned long) (y & 0xffffffffLL);
13774d99a5eSPaul Mundt 	low = sub3;
13874d99a5eSPaul Mundt 	high = 0LL;
13974d99a5eSPaul Mundt 	sub3 += (sub1 << 32);
14074d99a5eSPaul Mundt 	if (low > sub3)
14174d99a5eSPaul Mundt 		high++;
14274d99a5eSPaul Mundt 	low = sub3;
14374d99a5eSPaul Mundt 	sub3 += (sub2 << 32);
14474d99a5eSPaul Mundt 	if (low > sub3)
14574d99a5eSPaul Mundt 		high++;
14674d99a5eSPaul Mundt 	low = sub3;
14774d99a5eSPaul Mundt 	high += (sub1 >> 32) + (sub2 >> 32);
14874d99a5eSPaul Mundt 	high += sub0;
14974d99a5eSPaul Mundt 	*lowp = low;
15074d99a5eSPaul Mundt 	*highp = high;
15174d99a5eSPaul Mundt }
15274d99a5eSPaul Mundt 
rshift64(unsigned long long mh,unsigned long long ml,int n)15374d99a5eSPaul Mundt static inline long long rshift64(unsigned long long mh,
15474d99a5eSPaul Mundt 		unsigned long long ml, int n)
15574d99a5eSPaul Mundt {
15674d99a5eSPaul Mundt 	if (n >= 64)
15774d99a5eSPaul Mundt 		return mh >> (n - 64);
15874d99a5eSPaul Mundt 	return (mh << (64 - n)) | (ml >> n);
15974d99a5eSPaul Mundt }
16074d99a5eSPaul Mundt 
denormal_muld(long long hx,long long hy)16174d99a5eSPaul Mundt static long long denormal_muld(long long hx, long long hy)
16274d99a5eSPaul Mundt {
16374d99a5eSPaul Mundt 	unsigned long long ix, iy;
16474d99a5eSPaul Mundt 	unsigned long long mh, ml, nh, nl;
16574d99a5eSPaul Mundt 	int exp, w;
16674d99a5eSPaul Mundt 
16774d99a5eSPaul Mundt 	ix = hx & 0x7fffffffffffffffLL;
16874d99a5eSPaul Mundt 	iy = hy & 0x7fffffffffffffffLL;
16974d99a5eSPaul Mundt 	if (iy < 0x0010000000000000LL || ix == 0)
17074d99a5eSPaul Mundt 		return ((hx ^ hy) & 0x8000000000000000LL);
17174d99a5eSPaul Mundt 
17274d99a5eSPaul Mundt 	exp = (iy & 0x7ff0000000000000LL) >> 52;
17374d99a5eSPaul Mundt 	ix &= 0x000fffffffffffffLL;
17474d99a5eSPaul Mundt 	iy = (iy & 0x000fffffffffffffLL) | 0x0010000000000000LL;
17574d99a5eSPaul Mundt 	mult64(ix, iy, &mh, &ml);
17674d99a5eSPaul Mundt 	nh = mh;
17774d99a5eSPaul Mundt 	nl = ml;
17874d99a5eSPaul Mundt 	w = -1;
17974d99a5eSPaul Mundt 	if (nh) {
18074d99a5eSPaul Mundt 		while (nh) { nh >>= 1; w++;}
18174d99a5eSPaul Mundt 		w += 64;
18274d99a5eSPaul Mundt 	} else
18374d99a5eSPaul Mundt 		while (nl) { nl >>= 1; w++;}
18474d99a5eSPaul Mundt 
18574d99a5eSPaul Mundt 	/* FIXME: use guard bits */
18674d99a5eSPaul Mundt 	exp += w - 1022 - 52 * 2;
18774d99a5eSPaul Mundt 	if (exp > 0)
18874d99a5eSPaul Mundt 		ix = (rshift64(mh, ml, w - 52) & 0x000fffffffffffffLL)
18974d99a5eSPaul Mundt 			| ((long long)exp << 52);
19074d99a5eSPaul Mundt 	else if (exp + 51 >= 0)
19174d99a5eSPaul Mundt 		ix = rshift64(mh, ml, w - 51 - exp) & 0x000fffffffffffffLL;
19274d99a5eSPaul Mundt 	else
19374d99a5eSPaul Mundt 		ix = 0;
19474d99a5eSPaul Mundt 
19574d99a5eSPaul Mundt 	ix |= (hx ^ hy) & 0x8000000000000000LL;
19674d99a5eSPaul Mundt 	return ix;
19774d99a5eSPaul Mundt }
19874d99a5eSPaul Mundt 
19974d99a5eSPaul Mundt /* ix - iy where iy: denormal and ix, iy >= 0 */
denormal_subf1(unsigned int ix,unsigned int iy)20074d99a5eSPaul Mundt static int denormal_subf1(unsigned int ix, unsigned int iy)
20174d99a5eSPaul Mundt {
20274d99a5eSPaul Mundt 	int frac;
20374d99a5eSPaul Mundt 	int exp;
20474d99a5eSPaul Mundt 
20574d99a5eSPaul Mundt 	if (ix < 0x00800000)
20674d99a5eSPaul Mundt 		return ix - iy;
20774d99a5eSPaul Mundt 
20874d99a5eSPaul Mundt 	exp = (ix & 0x7f800000) >> 23;
20974d99a5eSPaul Mundt 	if (exp - 1 > 31)
21074d99a5eSPaul Mundt 		return ix;
21174d99a5eSPaul Mundt 	iy >>= exp - 1;
21274d99a5eSPaul Mundt 	if (iy == 0)
21374d99a5eSPaul Mundt 		return ix;
21474d99a5eSPaul Mundt 
21574d99a5eSPaul Mundt 	frac = (ix & 0x007fffff) | 0x00800000;
21674d99a5eSPaul Mundt 	frac -= iy;
21774d99a5eSPaul Mundt 	while (frac < 0x00800000) {
21874d99a5eSPaul Mundt 		if (--exp == 0)
21974d99a5eSPaul Mundt 			return frac;
22074d99a5eSPaul Mundt 		frac <<= 1;
22174d99a5eSPaul Mundt 	}
22274d99a5eSPaul Mundt 
22374d99a5eSPaul Mundt 	return (exp << 23) | (frac & 0x007fffff);
22474d99a5eSPaul Mundt }
22574d99a5eSPaul Mundt 
22674d99a5eSPaul Mundt /* ix + iy where iy: denormal and ix, iy >= 0 */
denormal_addf1(unsigned int ix,unsigned int iy)22774d99a5eSPaul Mundt static int denormal_addf1(unsigned int ix, unsigned int iy)
22874d99a5eSPaul Mundt {
22974d99a5eSPaul Mundt 	int frac;
23074d99a5eSPaul Mundt 	int exp;
23174d99a5eSPaul Mundt 
23274d99a5eSPaul Mundt 	if (ix < 0x00800000)
23374d99a5eSPaul Mundt 		return ix + iy;
23474d99a5eSPaul Mundt 
23574d99a5eSPaul Mundt 	exp = (ix & 0x7f800000) >> 23;
23674d99a5eSPaul Mundt 	if (exp - 1 > 31)
23774d99a5eSPaul Mundt 		return ix;
23874d99a5eSPaul Mundt 	iy >>= exp - 1;
23974d99a5eSPaul Mundt 	if (iy == 0)
24074d99a5eSPaul Mundt 	  return ix;
24174d99a5eSPaul Mundt 
24274d99a5eSPaul Mundt 	frac = (ix & 0x007fffff) | 0x00800000;
24374d99a5eSPaul Mundt 	frac += iy;
24474d99a5eSPaul Mundt 	if (frac >= 0x01000000) {
24574d99a5eSPaul Mundt 		frac >>= 1;
24674d99a5eSPaul Mundt 		++exp;
24774d99a5eSPaul Mundt 	}
24874d99a5eSPaul Mundt 
24974d99a5eSPaul Mundt 	return (exp << 23) | (frac & 0x007fffff);
25074d99a5eSPaul Mundt }
25174d99a5eSPaul Mundt 
denormal_addf(int hx,int hy)25274d99a5eSPaul Mundt static int denormal_addf(int hx, int hy)
25374d99a5eSPaul Mundt {
25474d99a5eSPaul Mundt 	unsigned int ix, iy;
25574d99a5eSPaul Mundt 	int sign;
25674d99a5eSPaul Mundt 
25774d99a5eSPaul Mundt 	if ((hx ^ hy) & 0x80000000) {
25874d99a5eSPaul Mundt 		sign = hx & 0x80000000;
25974d99a5eSPaul Mundt 		ix = hx & 0x7fffffff;
26074d99a5eSPaul Mundt 		iy = hy & 0x7fffffff;
26174d99a5eSPaul Mundt 		if (iy < 0x00800000) {
26274d99a5eSPaul Mundt 			ix = denormal_subf1(ix, iy);
2639731e287SRoel Kluin 			if ((int) ix < 0) {
26474d99a5eSPaul Mundt 				ix = -ix;
26574d99a5eSPaul Mundt 				sign ^= 0x80000000;
26674d99a5eSPaul Mundt 			}
26774d99a5eSPaul Mundt 		} else {
26874d99a5eSPaul Mundt 			ix = denormal_subf1(iy, ix);
26974d99a5eSPaul Mundt 			sign ^= 0x80000000;
27074d99a5eSPaul Mundt 		}
27174d99a5eSPaul Mundt 	} else {
27274d99a5eSPaul Mundt 		sign = hx & 0x80000000;
27374d99a5eSPaul Mundt 		ix = hx & 0x7fffffff;
27474d99a5eSPaul Mundt 		iy = hy & 0x7fffffff;
27574d99a5eSPaul Mundt 		if (iy < 0x00800000)
27674d99a5eSPaul Mundt 			ix = denormal_addf1(ix, iy);
27774d99a5eSPaul Mundt 		else
27874d99a5eSPaul Mundt 			ix = denormal_addf1(iy, ix);
27974d99a5eSPaul Mundt 	}
28074d99a5eSPaul Mundt 
28174d99a5eSPaul Mundt 	return sign | ix;
28274d99a5eSPaul Mundt }
28374d99a5eSPaul Mundt 
28474d99a5eSPaul Mundt /* ix - iy where iy: denormal and ix, iy >= 0 */
denormal_subd1(unsigned long long ix,unsigned long long iy)28574d99a5eSPaul Mundt static long long denormal_subd1(unsigned long long ix, unsigned long long iy)
28674d99a5eSPaul Mundt {
28774d99a5eSPaul Mundt 	long long frac;
28874d99a5eSPaul Mundt 	int exp;
28974d99a5eSPaul Mundt 
29074d99a5eSPaul Mundt 	if (ix < 0x0010000000000000LL)
29174d99a5eSPaul Mundt 		return ix - iy;
29274d99a5eSPaul Mundt 
29374d99a5eSPaul Mundt 	exp = (ix & 0x7ff0000000000000LL) >> 52;
29474d99a5eSPaul Mundt 	if (exp - 1 > 63)
29574d99a5eSPaul Mundt 		return ix;
29674d99a5eSPaul Mundt 	iy >>= exp - 1;
29774d99a5eSPaul Mundt 	if (iy == 0)
29874d99a5eSPaul Mundt 		return ix;
29974d99a5eSPaul Mundt 
30074d99a5eSPaul Mundt 	frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL;
30174d99a5eSPaul Mundt 	frac -= iy;
30274d99a5eSPaul Mundt 	while (frac < 0x0010000000000000LL) {
30374d99a5eSPaul Mundt 		if (--exp == 0)
30474d99a5eSPaul Mundt 			return frac;
30574d99a5eSPaul Mundt 		frac <<= 1;
30674d99a5eSPaul Mundt 	}
30774d99a5eSPaul Mundt 
30874d99a5eSPaul Mundt 	return ((long long)exp << 52) | (frac & 0x000fffffffffffffLL);
30974d99a5eSPaul Mundt }
31074d99a5eSPaul Mundt 
31174d99a5eSPaul Mundt /* ix + iy where iy: denormal and ix, iy >= 0 */
denormal_addd1(unsigned long long ix,unsigned long long iy)31274d99a5eSPaul Mundt static long long denormal_addd1(unsigned long long ix, unsigned long long iy)
31374d99a5eSPaul Mundt {
31474d99a5eSPaul Mundt 	long long frac;
31574d99a5eSPaul Mundt 	long long exp;
31674d99a5eSPaul Mundt 
31774d99a5eSPaul Mundt 	if (ix < 0x0010000000000000LL)
31874d99a5eSPaul Mundt 		return ix + iy;
31974d99a5eSPaul Mundt 
32074d99a5eSPaul Mundt 	exp = (ix & 0x7ff0000000000000LL) >> 52;
32174d99a5eSPaul Mundt 	if (exp - 1 > 63)
32274d99a5eSPaul Mundt 		return ix;
32374d99a5eSPaul Mundt 	iy >>= exp - 1;
32474d99a5eSPaul Mundt 	if (iy == 0)
32574d99a5eSPaul Mundt 	  return ix;
32674d99a5eSPaul Mundt 
32774d99a5eSPaul Mundt 	frac = (ix & 0x000fffffffffffffLL) | 0x0010000000000000LL;
32874d99a5eSPaul Mundt 	frac += iy;
32974d99a5eSPaul Mundt 	if (frac >= 0x0020000000000000LL) {
33074d99a5eSPaul Mundt 		frac >>= 1;
33174d99a5eSPaul Mundt 		++exp;
33274d99a5eSPaul Mundt 	}
33374d99a5eSPaul Mundt 
33474d99a5eSPaul Mundt 	return (exp << 52) | (frac & 0x000fffffffffffffLL);
33574d99a5eSPaul Mundt }
33674d99a5eSPaul Mundt 
denormal_addd(long long hx,long long hy)33774d99a5eSPaul Mundt static long long denormal_addd(long long hx, long long hy)
33874d99a5eSPaul Mundt {
33974d99a5eSPaul Mundt 	unsigned long long ix, iy;
34074d99a5eSPaul Mundt 	long long sign;
34174d99a5eSPaul Mundt 
34274d99a5eSPaul Mundt 	if ((hx ^ hy) & 0x8000000000000000LL) {
34374d99a5eSPaul Mundt 		sign = hx & 0x8000000000000000LL;
34474d99a5eSPaul Mundt 		ix = hx & 0x7fffffffffffffffLL;
34574d99a5eSPaul Mundt 		iy = hy & 0x7fffffffffffffffLL;
34674d99a5eSPaul Mundt 		if (iy < 0x0010000000000000LL) {
34774d99a5eSPaul Mundt 			ix = denormal_subd1(ix, iy);
3489731e287SRoel Kluin 			if ((int) ix < 0) {
34974d99a5eSPaul Mundt 				ix = -ix;
35074d99a5eSPaul Mundt 				sign ^= 0x8000000000000000LL;
35174d99a5eSPaul Mundt 			}
35274d99a5eSPaul Mundt 		} else {
35374d99a5eSPaul Mundt 			ix = denormal_subd1(iy, ix);
35474d99a5eSPaul Mundt 			sign ^= 0x8000000000000000LL;
35574d99a5eSPaul Mundt 		}
35674d99a5eSPaul Mundt 	} else {
35774d99a5eSPaul Mundt 		sign = hx & 0x8000000000000000LL;
35874d99a5eSPaul Mundt 		ix = hx & 0x7fffffffffffffffLL;
35974d99a5eSPaul Mundt 		iy = hy & 0x7fffffffffffffffLL;
36074d99a5eSPaul Mundt 		if (iy < 0x0010000000000000LL)
36174d99a5eSPaul Mundt 			ix = denormal_addd1(ix, iy);
36274d99a5eSPaul Mundt 		else
36374d99a5eSPaul Mundt 			ix = denormal_addd1(iy, ix);
36474d99a5eSPaul Mundt 	}
36574d99a5eSPaul Mundt 
36674d99a5eSPaul Mundt 	return sign | ix;
36774d99a5eSPaul Mundt }
36874d99a5eSPaul Mundt 
36974d99a5eSPaul Mundt /**
37074d99a5eSPaul Mundt  *	denormal_to_double - Given denormalized float number,
37174d99a5eSPaul Mundt  *	                     store double float
37274d99a5eSPaul Mundt  *
37374d99a5eSPaul Mundt  *	@fpu: Pointer to sh_fpu_hard structure
37474d99a5eSPaul Mundt  *	@n: Index to FP register
37574d99a5eSPaul Mundt  */
37674d99a5eSPaul Mundt static void
denormal_to_double(struct sh_fpu_hard_struct * fpu,int n)37774d99a5eSPaul Mundt denormal_to_double (struct sh_fpu_hard_struct *fpu, int n)
37874d99a5eSPaul Mundt {
37974d99a5eSPaul Mundt 	unsigned long du, dl;
38074d99a5eSPaul Mundt 	unsigned long x = fpu->fpul;
38174d99a5eSPaul Mundt 	int exp = 1023 - 126;
38274d99a5eSPaul Mundt 
38374d99a5eSPaul Mundt 	if (x != 0 && (x & 0x7f800000) == 0) {
38474d99a5eSPaul Mundt 		du = (x & 0x80000000);
38574d99a5eSPaul Mundt 		while ((x & 0x00800000) == 0) {
38674d99a5eSPaul Mundt 			x <<= 1;
38774d99a5eSPaul Mundt 			exp--;
38874d99a5eSPaul Mundt 		}
38974d99a5eSPaul Mundt 		x &= 0x007fffff;
39074d99a5eSPaul Mundt 		du |= (exp << 20) | (x >> 3);
39174d99a5eSPaul Mundt 		dl = x << 29;
39274d99a5eSPaul Mundt 
39374d99a5eSPaul Mundt 		fpu->fp_regs[n] = du;
39474d99a5eSPaul Mundt 		fpu->fp_regs[n+1] = dl;
39574d99a5eSPaul Mundt 	}
39674d99a5eSPaul Mundt }
39774d99a5eSPaul Mundt 
39874d99a5eSPaul Mundt /**
39974d99a5eSPaul Mundt  *	ieee_fpe_handler - Handle denormalized number exception
40074d99a5eSPaul Mundt  *
40174d99a5eSPaul Mundt  *	@regs: Pointer to register structure
40274d99a5eSPaul Mundt  *
40374d99a5eSPaul Mundt  *	Returns 1 when it's handled (should not cause exception).
40474d99a5eSPaul Mundt  */
40574d99a5eSPaul Mundt static int
ieee_fpe_handler(struct pt_regs * regs)40674d99a5eSPaul Mundt ieee_fpe_handler (struct pt_regs *regs)
40774d99a5eSPaul Mundt {
40874d99a5eSPaul Mundt 	unsigned short insn = *(unsigned short *) regs->pc;
40974d99a5eSPaul Mundt 	unsigned short finsn;
41074d99a5eSPaul Mundt 	unsigned long nextpc;
41174d99a5eSPaul Mundt 	int nib[4] = {
41274d99a5eSPaul Mundt 		(insn >> 12) & 0xf,
41374d99a5eSPaul Mundt 		(insn >> 8) & 0xf,
41474d99a5eSPaul Mundt 		(insn >> 4) & 0xf,
41574d99a5eSPaul Mundt 		insn & 0xf};
41674d99a5eSPaul Mundt 
41774d99a5eSPaul Mundt 	if (nib[0] == 0xb ||
41874d99a5eSPaul Mundt 	    (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb)) /* bsr & jsr */
41974d99a5eSPaul Mundt 		regs->pr = regs->pc + 4;
42074d99a5eSPaul Mundt 	if (nib[0] == 0xa || nib[0] == 0xb) { /* bra & bsr */
42174d99a5eSPaul Mundt 		nextpc = regs->pc + 4 + ((short) ((insn & 0xfff) << 4) >> 3);
42274d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
42374d99a5eSPaul Mundt 	} else if (nib[0] == 0x8 && nib[1] == 0xd) { /* bt/s */
42474d99a5eSPaul Mundt 		if (regs->sr & 1)
42574d99a5eSPaul Mundt 			nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
42674d99a5eSPaul Mundt 		else
42774d99a5eSPaul Mundt 			nextpc = regs->pc + 4;
42874d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
42974d99a5eSPaul Mundt 	} else if (nib[0] == 0x8 && nib[1] == 0xf) { /* bf/s */
43074d99a5eSPaul Mundt 		if (regs->sr & 1)
43174d99a5eSPaul Mundt 			nextpc = regs->pc + 4;
43274d99a5eSPaul Mundt 		else
43374d99a5eSPaul Mundt 			nextpc = regs->pc + 4 + ((char) (insn & 0xff) << 1);
43474d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
43574d99a5eSPaul Mundt 	} else if (nib[0] == 0x4 && nib[3] == 0xb &&
43674d99a5eSPaul Mundt 		 (nib[2] == 0x0 || nib[2] == 0x2)) { /* jmp & jsr */
43774d99a5eSPaul Mundt 		nextpc = regs->regs[nib[1]];
43874d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
43974d99a5eSPaul Mundt 	} else if (nib[0] == 0x0 && nib[3] == 0x3 &&
44074d99a5eSPaul Mundt 		 (nib[2] == 0x0 || nib[2] == 0x2)) { /* braf & bsrf */
44174d99a5eSPaul Mundt 		nextpc = regs->pc + 4 + regs->regs[nib[1]];
44274d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
44374d99a5eSPaul Mundt 	} else if (insn == 0x000b) { /* rts */
44474d99a5eSPaul Mundt 		nextpc = regs->pr;
44574d99a5eSPaul Mundt 		finsn = *(unsigned short *) (regs->pc + 2);
44674d99a5eSPaul Mundt 	} else {
44774d99a5eSPaul Mundt 		nextpc = regs->pc + 2;
44874d99a5eSPaul Mundt 		finsn = insn;
44974d99a5eSPaul Mundt 	}
45074d99a5eSPaul Mundt 
45174d99a5eSPaul Mundt #define FPSCR_FPU_ERROR (1 << 17)
45274d99a5eSPaul Mundt 
45374d99a5eSPaul Mundt 	if ((finsn & 0xf1ff) == 0xf0ad) { /* fcnvsd */
45474d99a5eSPaul Mundt 		struct task_struct *tsk = current;
45574d99a5eSPaul Mundt 
4560ea820cfSPaul Mundt 		if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_FPU_ERROR)) {
45774d99a5eSPaul Mundt 			/* FPU error */
4580ea820cfSPaul Mundt 			denormal_to_double (&tsk->thread.xstate->hardfpu,
45974d99a5eSPaul Mundt 					    (finsn >> 8) & 0xf);
46074d99a5eSPaul Mundt 		} else
46174d99a5eSPaul Mundt 			return 0;
46274d99a5eSPaul Mundt 
46374d99a5eSPaul Mundt 		regs->pc = nextpc;
46474d99a5eSPaul Mundt 		return 1;
46574d99a5eSPaul Mundt 	} else if ((finsn & 0xf00f) == 0xf002) { /* fmul */
46674d99a5eSPaul Mundt 		struct task_struct *tsk = current;
46774d99a5eSPaul Mundt 		int fpscr;
46874d99a5eSPaul Mundt 		int n, m, prec;
46974d99a5eSPaul Mundt 		unsigned int hx, hy;
47074d99a5eSPaul Mundt 
47174d99a5eSPaul Mundt 		n = (finsn >> 8) & 0xf;
47274d99a5eSPaul Mundt 		m = (finsn >> 4) & 0xf;
4730ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
4740ea820cfSPaul Mundt 		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
4750ea820cfSPaul Mundt 		fpscr = tsk->thread.xstate->hardfpu.fpscr;
47674d99a5eSPaul Mundt 		prec = fpscr & (1 << 19);
47774d99a5eSPaul Mundt 
47874d99a5eSPaul Mundt 		if ((fpscr & FPSCR_FPU_ERROR)
47974d99a5eSPaul Mundt 		     && (prec && ((hx & 0x7fffffff) < 0x00100000
48074d99a5eSPaul Mundt 				   || (hy & 0x7fffffff) < 0x00100000))) {
48174d99a5eSPaul Mundt 			long long llx, lly;
48274d99a5eSPaul Mundt 
48374d99a5eSPaul Mundt 			/* FPU error because of denormal */
48474d99a5eSPaul Mundt 			llx = ((long long) hx << 32)
4850ea820cfSPaul Mundt 			       | tsk->thread.xstate->hardfpu.fp_regs[n+1];
48674d99a5eSPaul Mundt 			lly = ((long long) hy << 32)
4870ea820cfSPaul Mundt 			       | tsk->thread.xstate->hardfpu.fp_regs[m+1];
48874d99a5eSPaul Mundt 			if ((hx & 0x7fffffff) >= 0x00100000)
48974d99a5eSPaul Mundt 				llx = denormal_muld(lly, llx);
49074d99a5eSPaul Mundt 			else
49174d99a5eSPaul Mundt 				llx = denormal_muld(llx, lly);
4920ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
4930ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff;
49474d99a5eSPaul Mundt 		} else if ((fpscr & FPSCR_FPU_ERROR)
49574d99a5eSPaul Mundt 		     && (!prec && ((hx & 0x7fffffff) < 0x00800000
49674d99a5eSPaul Mundt 				   || (hy & 0x7fffffff) < 0x00800000))) {
49774d99a5eSPaul Mundt 			/* FPU error because of denormal */
49874d99a5eSPaul Mundt 			if ((hx & 0x7fffffff) >= 0x00800000)
49974d99a5eSPaul Mundt 				hx = denormal_mulf(hy, hx);
50074d99a5eSPaul Mundt 			else
50174d99a5eSPaul Mundt 				hx = denormal_mulf(hx, hy);
5020ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
50374d99a5eSPaul Mundt 		} else
50474d99a5eSPaul Mundt 			return 0;
50574d99a5eSPaul Mundt 
50674d99a5eSPaul Mundt 		regs->pc = nextpc;
50774d99a5eSPaul Mundt 		return 1;
50874d99a5eSPaul Mundt 	} else if ((finsn & 0xf00e) == 0xf000) { /* fadd, fsub */
50974d99a5eSPaul Mundt 		struct task_struct *tsk = current;
51074d99a5eSPaul Mundt 		int fpscr;
51174d99a5eSPaul Mundt 		int n, m, prec;
51274d99a5eSPaul Mundt 		unsigned int hx, hy;
51374d99a5eSPaul Mundt 
51474d99a5eSPaul Mundt 		n = (finsn >> 8) & 0xf;
51574d99a5eSPaul Mundt 		m = (finsn >> 4) & 0xf;
5160ea820cfSPaul Mundt 		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
5170ea820cfSPaul Mundt 		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
5180ea820cfSPaul Mundt 		fpscr = tsk->thread.xstate->hardfpu.fpscr;
51974d99a5eSPaul Mundt 		prec = fpscr & (1 << 19);
52074d99a5eSPaul Mundt 
52174d99a5eSPaul Mundt 		if ((fpscr & FPSCR_FPU_ERROR)
52274d99a5eSPaul Mundt 		     && (prec && ((hx & 0x7fffffff) < 0x00100000
52374d99a5eSPaul Mundt 				   || (hy & 0x7fffffff) < 0x00100000))) {
52474d99a5eSPaul Mundt 			long long llx, lly;
52574d99a5eSPaul Mundt 
52674d99a5eSPaul Mundt 			/* FPU error because of denormal */
52774d99a5eSPaul Mundt 			llx = ((long long) hx << 32)
5280ea820cfSPaul Mundt 			       | tsk->thread.xstate->hardfpu.fp_regs[n+1];
52974d99a5eSPaul Mundt 			lly = ((long long) hy << 32)
5300ea820cfSPaul Mundt 			       | tsk->thread.xstate->hardfpu.fp_regs[m+1];
53174d99a5eSPaul Mundt 			if ((finsn & 0xf00f) == 0xf000)
53274d99a5eSPaul Mundt 				llx = denormal_addd(llx, lly);
53374d99a5eSPaul Mundt 			else
53474d99a5eSPaul Mundt 				llx = denormal_addd(llx, lly ^ (1LL << 63));
5350ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
5360ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n+1] = llx & 0xffffffff;
53774d99a5eSPaul Mundt 		} else if ((fpscr & FPSCR_FPU_ERROR)
53874d99a5eSPaul Mundt 		     && (!prec && ((hx & 0x7fffffff) < 0x00800000
53974d99a5eSPaul Mundt 				   || (hy & 0x7fffffff) < 0x00800000))) {
54074d99a5eSPaul Mundt 			/* FPU error because of denormal */
54174d99a5eSPaul Mundt 			if ((finsn & 0xf00f) == 0xf000)
54274d99a5eSPaul Mundt 				hx = denormal_addf(hx, hy);
54374d99a5eSPaul Mundt 			else
54474d99a5eSPaul Mundt 				hx = denormal_addf(hx, hy ^ 0x80000000);
5450ea820cfSPaul Mundt 			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
54674d99a5eSPaul Mundt 		} else
54774d99a5eSPaul Mundt 			return 0;
54874d99a5eSPaul Mundt 
54974d99a5eSPaul Mundt 		regs->pc = nextpc;
55074d99a5eSPaul Mundt 		return 1;
55174d99a5eSPaul Mundt 	}
55274d99a5eSPaul Mundt 
55374d99a5eSPaul Mundt 	return 0;
55474d99a5eSPaul Mundt }
55574d99a5eSPaul Mundt 
BUILD_TRAP_HANDLER(fpu_error)55674d99a5eSPaul Mundt BUILD_TRAP_HANDLER(fpu_error)
55774d99a5eSPaul Mundt {
55874d99a5eSPaul Mundt 	struct task_struct *tsk = current;
55974d99a5eSPaul Mundt 	TRAP_HANDLER_DECL;
56074d99a5eSPaul Mundt 
561d3ea9fa0SStuart Menefy 	__unlazy_fpu(tsk, regs);
56274d99a5eSPaul Mundt 	if (ieee_fpe_handler(regs)) {
5630ea820cfSPaul Mundt 		tsk->thread.xstate->hardfpu.fpscr &=
56474d99a5eSPaul Mundt 			~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
56574d99a5eSPaul Mundt 		grab_fpu(regs);
56674d99a5eSPaul Mundt 		restore_fpu(tsk);
567d3ea9fa0SStuart Menefy 		task_thread_info(tsk)->status |= TS_USEDFPU;
56874d99a5eSPaul Mundt 		return;
56974d99a5eSPaul Mundt 	}
57074d99a5eSPaul Mundt 
5713cf5d076SEric W. Biederman 	force_sig(SIGFPE);
57274d99a5eSPaul Mundt }
573