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