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