xref: /openbmc/linux/arch/powerpc/math-emu/math_efp.c (revision 59dc5bfc)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26a800f36SLiu Yu /*
36a800f36SLiu Yu  * arch/powerpc/math-emu/math_efp.c
46a800f36SLiu Yu  *
5ac6f1203SLiu Yu  * Copyright (C) 2006-2008, 2010 Freescale Semiconductor, Inc.
66a800f36SLiu Yu  *
76a800f36SLiu Yu  * Author: Ebony Zhu,	<ebony.zhu@freescale.com>
86a800f36SLiu Yu  *         Yu Liu,	<yu.liu@freescale.com>
96a800f36SLiu Yu  *
106a800f36SLiu Yu  * Derived from arch/alpha/math-emu/math.c
116a800f36SLiu Yu  *              arch/powerpc/math-emu/math.c
126a800f36SLiu Yu  *
136a800f36SLiu Yu  * Description:
146a800f36SLiu Yu  * This file is the exception handler to make E500 SPE instructions
156a800f36SLiu Yu  * fully comply with IEEE-754 floating point standard.
166a800f36SLiu Yu  */
176a800f36SLiu Yu 
186a800f36SLiu Yu #include <linux/types.h>
1901c9cceeSJoseph Myers #include <linux/prctl.h>
206a800f36SLiu Yu 
217c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
226a800f36SLiu Yu #include <asm/reg.h>
236a800f36SLiu Yu 
246a800f36SLiu Yu #define FP_EX_BOOKE_E500_SPE
256a800f36SLiu Yu #include <asm/sfp-machine.h>
266a800f36SLiu Yu 
276a800f36SLiu Yu #include <math-emu/soft-fp.h>
286a800f36SLiu Yu #include <math-emu/single.h>
296a800f36SLiu Yu #include <math-emu/double.h>
306a800f36SLiu Yu 
316a800f36SLiu Yu #define EFAPU		0x4
326a800f36SLiu Yu 
336a800f36SLiu Yu #define VCT		0x4
346a800f36SLiu Yu #define SPFP		0x6
356a800f36SLiu Yu #define DPFP		0x7
366a800f36SLiu Yu 
376a800f36SLiu Yu #define EFSADD		0x2c0
386a800f36SLiu Yu #define EFSSUB		0x2c1
396a800f36SLiu Yu #define EFSABS		0x2c4
406a800f36SLiu Yu #define EFSNABS		0x2c5
416a800f36SLiu Yu #define EFSNEG		0x2c6
426a800f36SLiu Yu #define EFSMUL		0x2c8
436a800f36SLiu Yu #define EFSDIV		0x2c9
446a800f36SLiu Yu #define EFSCMPGT	0x2cc
456a800f36SLiu Yu #define EFSCMPLT	0x2cd
466a800f36SLiu Yu #define EFSCMPEQ	0x2ce
476a800f36SLiu Yu #define EFSCFD		0x2cf
486a800f36SLiu Yu #define EFSCFSI		0x2d1
496a800f36SLiu Yu #define EFSCTUI		0x2d4
506a800f36SLiu Yu #define EFSCTSI		0x2d5
516a800f36SLiu Yu #define EFSCTUF		0x2d6
526a800f36SLiu Yu #define EFSCTSF		0x2d7
536a800f36SLiu Yu #define EFSCTUIZ	0x2d8
546a800f36SLiu Yu #define EFSCTSIZ	0x2da
556a800f36SLiu Yu 
566a800f36SLiu Yu #define EVFSADD		0x280
576a800f36SLiu Yu #define EVFSSUB		0x281
586a800f36SLiu Yu #define EVFSABS		0x284
596a800f36SLiu Yu #define EVFSNABS	0x285
606a800f36SLiu Yu #define EVFSNEG		0x286
616a800f36SLiu Yu #define EVFSMUL		0x288
626a800f36SLiu Yu #define EVFSDIV		0x289
636a800f36SLiu Yu #define EVFSCMPGT	0x28c
646a800f36SLiu Yu #define EVFSCMPLT	0x28d
656a800f36SLiu Yu #define EVFSCMPEQ	0x28e
666a800f36SLiu Yu #define EVFSCTUI	0x294
676a800f36SLiu Yu #define EVFSCTSI	0x295
686a800f36SLiu Yu #define EVFSCTUF	0x296
696a800f36SLiu Yu #define EVFSCTSF	0x297
706a800f36SLiu Yu #define EVFSCTUIZ	0x298
716a800f36SLiu Yu #define EVFSCTSIZ	0x29a
726a800f36SLiu Yu 
736a800f36SLiu Yu #define EFDADD		0x2e0
746a800f36SLiu Yu #define EFDSUB		0x2e1
756a800f36SLiu Yu #define EFDABS		0x2e4
766a800f36SLiu Yu #define EFDNABS		0x2e5
776a800f36SLiu Yu #define EFDNEG		0x2e6
786a800f36SLiu Yu #define EFDMUL		0x2e8
796a800f36SLiu Yu #define EFDDIV		0x2e9
806a800f36SLiu Yu #define EFDCTUIDZ	0x2ea
816a800f36SLiu Yu #define EFDCTSIDZ	0x2eb
826a800f36SLiu Yu #define EFDCMPGT	0x2ec
836a800f36SLiu Yu #define EFDCMPLT	0x2ed
846a800f36SLiu Yu #define EFDCMPEQ	0x2ee
856a800f36SLiu Yu #define EFDCFS		0x2ef
866a800f36SLiu Yu #define EFDCTUI		0x2f4
876a800f36SLiu Yu #define EFDCTSI		0x2f5
886a800f36SLiu Yu #define EFDCTUF		0x2f6
896a800f36SLiu Yu #define EFDCTSF		0x2f7
906a800f36SLiu Yu #define EFDCTUIZ	0x2f8
916a800f36SLiu Yu #define EFDCTSIZ	0x2fa
926a800f36SLiu Yu 
936a800f36SLiu Yu #define AB	2
946a800f36SLiu Yu #define XA	3
956a800f36SLiu Yu #define XB	4
966a800f36SLiu Yu #define XCR	5
976a800f36SLiu Yu #define NOTYPE	0
986a800f36SLiu Yu 
996a800f36SLiu Yu #define SIGN_BIT_S	(1UL << 31)
1006a800f36SLiu Yu #define SIGN_BIT_D	(1ULL << 63)
1016a800f36SLiu Yu #define FP_EX_MASK	(FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \
1026a800f36SLiu Yu 			FP_EX_UNDERFLOW | FP_EX_OVERFLOW)
1036a800f36SLiu Yu 
104ac6f1203SLiu Yu static int have_e500_cpu_a005_erratum;
105ac6f1203SLiu Yu 
1066a800f36SLiu Yu union dw_union {
1076a800f36SLiu Yu 	u64 dp[1];
1086a800f36SLiu Yu 	u32 wp[2];
1096a800f36SLiu Yu };
1106a800f36SLiu Yu 
1116a800f36SLiu Yu static unsigned long insn_type(unsigned long speinsn)
1126a800f36SLiu Yu {
1136a800f36SLiu Yu 	unsigned long ret = NOTYPE;
1146a800f36SLiu Yu 
1156a800f36SLiu Yu 	switch (speinsn & 0x7ff) {
1166a800f36SLiu Yu 	case EFSABS:	ret = XA;	break;
1176a800f36SLiu Yu 	case EFSADD:	ret = AB;	break;
1186a800f36SLiu Yu 	case EFSCFD:	ret = XB;	break;
1196a800f36SLiu Yu 	case EFSCMPEQ:	ret = XCR;	break;
1206a800f36SLiu Yu 	case EFSCMPGT:	ret = XCR;	break;
1216a800f36SLiu Yu 	case EFSCMPLT:	ret = XCR;	break;
1226a800f36SLiu Yu 	case EFSCTSF:	ret = XB;	break;
1236a800f36SLiu Yu 	case EFSCTSI:	ret = XB;	break;
1246a800f36SLiu Yu 	case EFSCTSIZ:	ret = XB;	break;
1256a800f36SLiu Yu 	case EFSCTUF:	ret = XB;	break;
1266a800f36SLiu Yu 	case EFSCTUI:	ret = XB;	break;
1276a800f36SLiu Yu 	case EFSCTUIZ:	ret = XB;	break;
1286a800f36SLiu Yu 	case EFSDIV:	ret = AB;	break;
1296a800f36SLiu Yu 	case EFSMUL:	ret = AB;	break;
1306a800f36SLiu Yu 	case EFSNABS:	ret = XA;	break;
1316a800f36SLiu Yu 	case EFSNEG:	ret = XA;	break;
1326a800f36SLiu Yu 	case EFSSUB:	ret = AB;	break;
1336a800f36SLiu Yu 	case EFSCFSI:	ret = XB;	break;
1346a800f36SLiu Yu 
1356a800f36SLiu Yu 	case EVFSABS:	ret = XA;	break;
1366a800f36SLiu Yu 	case EVFSADD:	ret = AB;	break;
1376a800f36SLiu Yu 	case EVFSCMPEQ:	ret = XCR;	break;
1386a800f36SLiu Yu 	case EVFSCMPGT:	ret = XCR;	break;
1396a800f36SLiu Yu 	case EVFSCMPLT:	ret = XCR;	break;
1406a800f36SLiu Yu 	case EVFSCTSF:	ret = XB;	break;
1416a800f36SLiu Yu 	case EVFSCTSI:	ret = XB;	break;
1426a800f36SLiu Yu 	case EVFSCTSIZ:	ret = XB;	break;
1436a800f36SLiu Yu 	case EVFSCTUF:	ret = XB;	break;
1446a800f36SLiu Yu 	case EVFSCTUI:	ret = XB;	break;
1456a800f36SLiu Yu 	case EVFSCTUIZ:	ret = XB;	break;
1466a800f36SLiu Yu 	case EVFSDIV:	ret = AB;	break;
1476a800f36SLiu Yu 	case EVFSMUL:	ret = AB;	break;
1486a800f36SLiu Yu 	case EVFSNABS:	ret = XA;	break;
1496a800f36SLiu Yu 	case EVFSNEG:	ret = XA;	break;
1506a800f36SLiu Yu 	case EVFSSUB:	ret = AB;	break;
1516a800f36SLiu Yu 
1526a800f36SLiu Yu 	case EFDABS:	ret = XA;	break;
1536a800f36SLiu Yu 	case EFDADD:	ret = AB;	break;
1546a800f36SLiu Yu 	case EFDCFS:	ret = XB;	break;
1556a800f36SLiu Yu 	case EFDCMPEQ:	ret = XCR;	break;
1566a800f36SLiu Yu 	case EFDCMPGT:	ret = XCR;	break;
1576a800f36SLiu Yu 	case EFDCMPLT:	ret = XCR;	break;
1586a800f36SLiu Yu 	case EFDCTSF:	ret = XB;	break;
1596a800f36SLiu Yu 	case EFDCTSI:	ret = XB;	break;
1606a800f36SLiu Yu 	case EFDCTSIDZ:	ret = XB;	break;
1616a800f36SLiu Yu 	case EFDCTSIZ:	ret = XB;	break;
1626a800f36SLiu Yu 	case EFDCTUF:	ret = XB;	break;
1636a800f36SLiu Yu 	case EFDCTUI:	ret = XB;	break;
1646a800f36SLiu Yu 	case EFDCTUIDZ:	ret = XB;	break;
1656a800f36SLiu Yu 	case EFDCTUIZ:	ret = XB;	break;
1666a800f36SLiu Yu 	case EFDDIV:	ret = AB;	break;
1676a800f36SLiu Yu 	case EFDMUL:	ret = AB;	break;
1686a800f36SLiu Yu 	case EFDNABS:	ret = XA;	break;
1696a800f36SLiu Yu 	case EFDNEG:	ret = XA;	break;
1706a800f36SLiu Yu 	case EFDSUB:	ret = AB;	break;
1716a800f36SLiu Yu 	}
1726a800f36SLiu Yu 
1736a800f36SLiu Yu 	return ret;
1746a800f36SLiu Yu }
1756a800f36SLiu Yu 
1766a800f36SLiu Yu int do_spe_mathemu(struct pt_regs *regs)
1776a800f36SLiu Yu {
1786a800f36SLiu Yu 	FP_DECL_EX;
1796a800f36SLiu Yu 	int IR, cmp;
1806a800f36SLiu Yu 
1816a800f36SLiu Yu 	unsigned long type, func, fc, fa, fb, src, speinsn;
1826a800f36SLiu Yu 	union dw_union vc, va, vb;
1836a800f36SLiu Yu 
1846a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
1856a800f36SLiu Yu 		return -EFAULT;
1866a800f36SLiu Yu 	if ((speinsn >> 26) != EFAPU)
1876a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
1886a800f36SLiu Yu 
1896a800f36SLiu Yu 	type = insn_type(speinsn);
1906a800f36SLiu Yu 	if (type == NOTYPE)
19109af52f7SLiu Yu 		goto illegal;
1926a800f36SLiu Yu 
1936a800f36SLiu Yu 	func = speinsn & 0x7ff;
1946a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
1956a800f36SLiu Yu 	fa = (speinsn >> 16) & 0x1f;
1966a800f36SLiu Yu 	fb = (speinsn >> 11) & 0x1f;
1976a800f36SLiu Yu 	src = (speinsn >> 5) & 0x7;
1986a800f36SLiu Yu 
1996a800f36SLiu Yu 	vc.wp[0] = current->thread.evr[fc];
2006a800f36SLiu Yu 	vc.wp[1] = regs->gpr[fc];
2016a800f36SLiu Yu 	va.wp[0] = current->thread.evr[fa];
2026a800f36SLiu Yu 	va.wp[1] = regs->gpr[fa];
2036a800f36SLiu Yu 	vb.wp[0] = current->thread.evr[fb];
2046a800f36SLiu Yu 	vb.wp[1] = regs->gpr[fb];
2056a800f36SLiu Yu 
2066a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
2076a800f36SLiu Yu 
208b430abc4SLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
209b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
210b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
211b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
2126a800f36SLiu Yu 
2136a800f36SLiu Yu 	switch (src) {
2146a800f36SLiu Yu 	case SPFP: {
2156a800f36SLiu Yu 		FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
2166a800f36SLiu Yu 
2176a800f36SLiu Yu 		switch (type) {
2186a800f36SLiu Yu 		case AB:
2196a800f36SLiu Yu 		case XCR:
2206a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2216a800f36SLiu Yu 		case XB:
2226a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
2236a800f36SLiu Yu 			break;
2246a800f36SLiu Yu 		case XA:
2256a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2266a800f36SLiu Yu 			break;
2276a800f36SLiu Yu 		}
2286a800f36SLiu Yu 
229b430abc4SLiu Yu 		pr_debug("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c);
230b430abc4SLiu Yu 		pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);
2316a800f36SLiu Yu 
2326a800f36SLiu Yu 		switch (func) {
2336a800f36SLiu Yu 		case EFSABS:
2346a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
2356a800f36SLiu Yu 			goto update_regs;
2366a800f36SLiu Yu 
2376a800f36SLiu Yu 		case EFSNABS:
2386a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
2396a800f36SLiu Yu 			goto update_regs;
2406a800f36SLiu Yu 
2416a800f36SLiu Yu 		case EFSNEG:
2426a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
2436a800f36SLiu Yu 			goto update_regs;
2446a800f36SLiu Yu 
2456a800f36SLiu Yu 		case EFSADD:
2466a800f36SLiu Yu 			FP_ADD_S(SR, SA, SB);
2476a800f36SLiu Yu 			goto pack_s;
2486a800f36SLiu Yu 
2496a800f36SLiu Yu 		case EFSSUB:
2506a800f36SLiu Yu 			FP_SUB_S(SR, SA, SB);
2516a800f36SLiu Yu 			goto pack_s;
2526a800f36SLiu Yu 
2536a800f36SLiu Yu 		case EFSMUL:
2546a800f36SLiu Yu 			FP_MUL_S(SR, SA, SB);
2556a800f36SLiu Yu 			goto pack_s;
2566a800f36SLiu Yu 
2576a800f36SLiu Yu 		case EFSDIV:
2586a800f36SLiu Yu 			FP_DIV_S(SR, SA, SB);
2596a800f36SLiu Yu 			goto pack_s;
2606a800f36SLiu Yu 
2616a800f36SLiu Yu 		case EFSCMPEQ:
2626a800f36SLiu Yu 			cmp = 0;
2636a800f36SLiu Yu 			goto cmp_s;
2646a800f36SLiu Yu 
2656a800f36SLiu Yu 		case EFSCMPGT:
2666a800f36SLiu Yu 			cmp = 1;
2676a800f36SLiu Yu 			goto cmp_s;
2686a800f36SLiu Yu 
2696a800f36SLiu Yu 		case EFSCMPLT:
2706a800f36SLiu Yu 			cmp = -1;
2716a800f36SLiu Yu 			goto cmp_s;
2726a800f36SLiu Yu 
2736a800f36SLiu Yu 		case EFSCTSF:
2746a800f36SLiu Yu 		case EFSCTUF:
27528fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
27628fbf1d5SJoseph Myers 				vc.wp[1] = 0;
27728fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
27828fbf1d5SJoseph Myers 			} else {
27928fbf1d5SJoseph Myers 				SB_e += (func == EFSCTSF ? 31 : 32);
28028fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32,
28128fbf1d5SJoseph Myers 						(func == EFSCTSF));
2826a800f36SLiu Yu 			}
2836a800f36SLiu Yu 			goto update_regs;
2846a800f36SLiu Yu 
2856a800f36SLiu Yu 		case EFSCFD: {
2866a800f36SLiu Yu 			FP_DECL_D(DB);
2876a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
2886a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
289b430abc4SLiu Yu 
290b430abc4SLiu Yu 			pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
2916a800f36SLiu Yu 					DB_s, DB_f1, DB_f0, DB_e, DB_c);
292b430abc4SLiu Yu 
2936a800f36SLiu Yu 			FP_CONV(S, D, 1, 2, SR, DB);
2946a800f36SLiu Yu 			goto pack_s;
2956a800f36SLiu Yu 		}
2966a800f36SLiu Yu 
2976a800f36SLiu Yu 		case EFSCTSI:
2986a800f36SLiu Yu 		case EFSCTUI:
29928fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
30028fbf1d5SJoseph Myers 				vc.wp[1] = 0;
30128fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
3026a800f36SLiu Yu 			} else {
30328fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32,
30428fbf1d5SJoseph Myers 						((func & 0x3) != 0));
3056a800f36SLiu Yu 			}
30628fbf1d5SJoseph Myers 			goto update_regs;
30728fbf1d5SJoseph Myers 
30828fbf1d5SJoseph Myers 		case EFSCTSIZ:
30928fbf1d5SJoseph Myers 		case EFSCTUIZ:
31028fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
31128fbf1d5SJoseph Myers 				vc.wp[1] = 0;
31228fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
31328fbf1d5SJoseph Myers 			} else {
314afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[1], SB, 32,
31528fbf1d5SJoseph Myers 						((func & 0x3) != 0));
31628fbf1d5SJoseph Myers 			}
3176a800f36SLiu Yu 			goto update_regs;
3186a800f36SLiu Yu 
3196a800f36SLiu Yu 		default:
3206a800f36SLiu Yu 			goto illegal;
3216a800f36SLiu Yu 		}
3226a800f36SLiu Yu 		break;
3236a800f36SLiu Yu 
3246a800f36SLiu Yu pack_s:
325b430abc4SLiu Yu 		pr_debug("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c);
326b430abc4SLiu Yu 
3276a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR);
3286a800f36SLiu Yu 		goto update_regs;
3296a800f36SLiu Yu 
3306a800f36SLiu Yu cmp_s:
3316a800f36SLiu Yu 		FP_CMP_S(IR, SA, SB, 3);
3326a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB)))
3336a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
3346a800f36SLiu Yu 		if (IR == cmp) {
3356a800f36SLiu Yu 			IR = 0x4;
3366a800f36SLiu Yu 		} else {
3376a800f36SLiu Yu 			IR = 0;
3386a800f36SLiu Yu 		}
3396a800f36SLiu Yu 		goto update_ccr;
3406a800f36SLiu Yu 	}
3416a800f36SLiu Yu 
3426a800f36SLiu Yu 	case DPFP: {
3436a800f36SLiu Yu 		FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
3446a800f36SLiu Yu 
3456a800f36SLiu Yu 		switch (type) {
3466a800f36SLiu Yu 		case AB:
3476a800f36SLiu Yu 		case XCR:
3486a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3496a800f36SLiu Yu 		case XB:
3506a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3516a800f36SLiu Yu 			break;
3526a800f36SLiu Yu 		case XA:
3536a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3546a800f36SLiu Yu 			break;
3556a800f36SLiu Yu 		}
3566a800f36SLiu Yu 
357b430abc4SLiu Yu 		pr_debug("DA: %ld %08lx %08lx %ld (%ld)\n",
3586a800f36SLiu Yu 				DA_s, DA_f1, DA_f0, DA_e, DA_c);
359b430abc4SLiu Yu 		pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
3606a800f36SLiu Yu 				DB_s, DB_f1, DB_f0, DB_e, DB_c);
3616a800f36SLiu Yu 
3626a800f36SLiu Yu 		switch (func) {
3636a800f36SLiu Yu 		case EFDABS:
3646a800f36SLiu Yu 			vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
3656a800f36SLiu Yu 			goto update_regs;
3666a800f36SLiu Yu 
3676a800f36SLiu Yu 		case EFDNABS:
3686a800f36SLiu Yu 			vc.dp[0] = va.dp[0] | SIGN_BIT_D;
3696a800f36SLiu Yu 			goto update_regs;
3706a800f36SLiu Yu 
3716a800f36SLiu Yu 		case EFDNEG:
3726a800f36SLiu Yu 			vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
3736a800f36SLiu Yu 			goto update_regs;
3746a800f36SLiu Yu 
3756a800f36SLiu Yu 		case EFDADD:
3766a800f36SLiu Yu 			FP_ADD_D(DR, DA, DB);
3776a800f36SLiu Yu 			goto pack_d;
3786a800f36SLiu Yu 
3796a800f36SLiu Yu 		case EFDSUB:
3806a800f36SLiu Yu 			FP_SUB_D(DR, DA, DB);
3816a800f36SLiu Yu 			goto pack_d;
3826a800f36SLiu Yu 
3836a800f36SLiu Yu 		case EFDMUL:
3846a800f36SLiu Yu 			FP_MUL_D(DR, DA, DB);
3856a800f36SLiu Yu 			goto pack_d;
3866a800f36SLiu Yu 
3876a800f36SLiu Yu 		case EFDDIV:
3886a800f36SLiu Yu 			FP_DIV_D(DR, DA, DB);
3896a800f36SLiu Yu 			goto pack_d;
3906a800f36SLiu Yu 
3916a800f36SLiu Yu 		case EFDCMPEQ:
3926a800f36SLiu Yu 			cmp = 0;
3936a800f36SLiu Yu 			goto cmp_d;
3946a800f36SLiu Yu 
3956a800f36SLiu Yu 		case EFDCMPGT:
3966a800f36SLiu Yu 			cmp = 1;
3976a800f36SLiu Yu 			goto cmp_d;
3986a800f36SLiu Yu 
3996a800f36SLiu Yu 		case EFDCMPLT:
4006a800f36SLiu Yu 			cmp = -1;
4016a800f36SLiu Yu 			goto cmp_d;
4026a800f36SLiu Yu 
4036a800f36SLiu Yu 		case EFDCTSF:
4046a800f36SLiu Yu 		case EFDCTUF:
40528fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
40628fbf1d5SJoseph Myers 				vc.wp[1] = 0;
40728fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
40828fbf1d5SJoseph Myers 			} else {
40928fbf1d5SJoseph Myers 				DB_e += (func == EFDCTSF ? 31 : 32);
41028fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32,
41128fbf1d5SJoseph Myers 						(func == EFDCTSF));
4126a800f36SLiu Yu 			}
4136a800f36SLiu Yu 			goto update_regs;
4146a800f36SLiu Yu 
4156a800f36SLiu Yu 		case EFDCFS: {
4166a800f36SLiu Yu 			FP_DECL_S(SB);
4176a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
4186a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
419b430abc4SLiu Yu 
420b430abc4SLiu Yu 			pr_debug("SB: %ld %08lx %ld (%ld)\n",
4216a800f36SLiu Yu 					SB_s, SB_f, SB_e, SB_c);
422b430abc4SLiu Yu 
4236a800f36SLiu Yu 			FP_CONV(D, S, 2, 1, DR, SB);
4246a800f36SLiu Yu 			goto pack_d;
4256a800f36SLiu Yu 		}
4266a800f36SLiu Yu 
4276a800f36SLiu Yu 		case EFDCTUIDZ:
4286a800f36SLiu Yu 		case EFDCTSIDZ:
42928fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
43028fbf1d5SJoseph Myers 				vc.dp[0] = 0;
43128fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
43228fbf1d5SJoseph Myers 			} else {
43328fbf1d5SJoseph Myers 				FP_TO_INT_D(vc.dp[0], DB, 64,
43428fbf1d5SJoseph Myers 						((func & 0x1) == 0));
43528fbf1d5SJoseph Myers 			}
4366a800f36SLiu Yu 			goto update_regs;
4376a800f36SLiu Yu 
4386a800f36SLiu Yu 		case EFDCTUI:
4396a800f36SLiu Yu 		case EFDCTSI:
44028fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
44128fbf1d5SJoseph Myers 				vc.wp[1] = 0;
44228fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
44328fbf1d5SJoseph Myers 			} else {
44428fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32,
44528fbf1d5SJoseph Myers 						((func & 0x3) != 0));
44628fbf1d5SJoseph Myers 			}
44728fbf1d5SJoseph Myers 			goto update_regs;
44828fbf1d5SJoseph Myers 
4496a800f36SLiu Yu 		case EFDCTUIZ:
4506a800f36SLiu Yu 		case EFDCTSIZ:
45128fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
45228fbf1d5SJoseph Myers 				vc.wp[1] = 0;
45328fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
4546a800f36SLiu Yu 			} else {
455afc0a07dSShan Hai 				FP_TO_INT_D(vc.wp[1], DB, 32,
45628fbf1d5SJoseph Myers 						((func & 0x3) != 0));
45728fbf1d5SJoseph Myers 			}
4586a800f36SLiu Yu 			goto update_regs;
4596a800f36SLiu Yu 
4606a800f36SLiu Yu 		default:
4616a800f36SLiu Yu 			goto illegal;
4626a800f36SLiu Yu 		}
4636a800f36SLiu Yu 		break;
4646a800f36SLiu Yu 
4656a800f36SLiu Yu pack_d:
466b430abc4SLiu Yu 		pr_debug("DR: %ld %08lx %08lx %ld (%ld)\n",
4676a800f36SLiu Yu 				DR_s, DR_f1, DR_f0, DR_e, DR_c);
468b430abc4SLiu Yu 
4696a800f36SLiu Yu 		FP_PACK_DP(vc.dp, DR);
4706a800f36SLiu Yu 		goto update_regs;
4716a800f36SLiu Yu 
4726a800f36SLiu Yu cmp_d:
4736a800f36SLiu Yu 		FP_CMP_D(IR, DA, DB, 3);
4746a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
4756a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
4766a800f36SLiu Yu 		if (IR == cmp) {
4776a800f36SLiu Yu 			IR = 0x4;
4786a800f36SLiu Yu 		} else {
4796a800f36SLiu Yu 			IR = 0;
4806a800f36SLiu Yu 		}
4816a800f36SLiu Yu 		goto update_ccr;
4826a800f36SLiu Yu 
4836a800f36SLiu Yu 	}
4846a800f36SLiu Yu 
4856a800f36SLiu Yu 	case VCT: {
4866a800f36SLiu Yu 		FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
4876a800f36SLiu Yu 		FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
4886a800f36SLiu Yu 		int IR0, IR1;
4896a800f36SLiu Yu 
4906a800f36SLiu Yu 		switch (type) {
4916a800f36SLiu Yu 		case AB:
4926a800f36SLiu Yu 		case XCR:
4936a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
4946a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
4956a800f36SLiu Yu 		case XB:
4966a800f36SLiu Yu 			FP_UNPACK_SP(SB0, vb.wp);
4976a800f36SLiu Yu 			FP_UNPACK_SP(SB1, vb.wp + 1);
4986a800f36SLiu Yu 			break;
4996a800f36SLiu Yu 		case XA:
5006a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
5016a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
5026a800f36SLiu Yu 			break;
5036a800f36SLiu Yu 		}
5046a800f36SLiu Yu 
505b430abc4SLiu Yu 		pr_debug("SA0: %ld %08lx %ld (%ld)\n",
506b430abc4SLiu Yu 				SA0_s, SA0_f, SA0_e, SA0_c);
507b430abc4SLiu Yu 		pr_debug("SA1: %ld %08lx %ld (%ld)\n",
508b430abc4SLiu Yu 				SA1_s, SA1_f, SA1_e, SA1_c);
509b430abc4SLiu Yu 		pr_debug("SB0: %ld %08lx %ld (%ld)\n",
510b430abc4SLiu Yu 				SB0_s, SB0_f, SB0_e, SB0_c);
511b430abc4SLiu Yu 		pr_debug("SB1: %ld %08lx %ld (%ld)\n",
512b430abc4SLiu Yu 				SB1_s, SB1_f, SB1_e, SB1_c);
5136a800f36SLiu Yu 
5146a800f36SLiu Yu 		switch (func) {
5156a800f36SLiu Yu 		case EVFSABS:
5166a800f36SLiu Yu 			vc.wp[0] = va.wp[0] & ~SIGN_BIT_S;
5176a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
5186a800f36SLiu Yu 			goto update_regs;
5196a800f36SLiu Yu 
5206a800f36SLiu Yu 		case EVFSNABS:
5216a800f36SLiu Yu 			vc.wp[0] = va.wp[0] | SIGN_BIT_S;
5226a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
5236a800f36SLiu Yu 			goto update_regs;
5246a800f36SLiu Yu 
5256a800f36SLiu Yu 		case EVFSNEG:
5266a800f36SLiu Yu 			vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
5276a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
5286a800f36SLiu Yu 			goto update_regs;
5296a800f36SLiu Yu 
5306a800f36SLiu Yu 		case EVFSADD:
5316a800f36SLiu Yu 			FP_ADD_S(SR0, SA0, SB0);
5326a800f36SLiu Yu 			FP_ADD_S(SR1, SA1, SB1);
5336a800f36SLiu Yu 			goto pack_vs;
5346a800f36SLiu Yu 
5356a800f36SLiu Yu 		case EVFSSUB:
5366a800f36SLiu Yu 			FP_SUB_S(SR0, SA0, SB0);
5376a800f36SLiu Yu 			FP_SUB_S(SR1, SA1, SB1);
5386a800f36SLiu Yu 			goto pack_vs;
5396a800f36SLiu Yu 
5406a800f36SLiu Yu 		case EVFSMUL:
5416a800f36SLiu Yu 			FP_MUL_S(SR0, SA0, SB0);
5426a800f36SLiu Yu 			FP_MUL_S(SR1, SA1, SB1);
5436a800f36SLiu Yu 			goto pack_vs;
5446a800f36SLiu Yu 
5456a800f36SLiu Yu 		case EVFSDIV:
5466a800f36SLiu Yu 			FP_DIV_S(SR0, SA0, SB0);
5476a800f36SLiu Yu 			FP_DIV_S(SR1, SA1, SB1);
5486a800f36SLiu Yu 			goto pack_vs;
5496a800f36SLiu Yu 
5506a800f36SLiu Yu 		case EVFSCMPEQ:
5516a800f36SLiu Yu 			cmp = 0;
5526a800f36SLiu Yu 			goto cmp_vs;
5536a800f36SLiu Yu 
5546a800f36SLiu Yu 		case EVFSCMPGT:
5556a800f36SLiu Yu 			cmp = 1;
5566a800f36SLiu Yu 			goto cmp_vs;
5576a800f36SLiu Yu 
5586a800f36SLiu Yu 		case EVFSCMPLT:
5596a800f36SLiu Yu 			cmp = -1;
5606a800f36SLiu Yu 			goto cmp_vs;
5616a800f36SLiu Yu 
5626a800f36SLiu Yu 		case EVFSCTUF:
56328fbf1d5SJoseph Myers 		case EVFSCTSF:
56428fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
56528fbf1d5SJoseph Myers 				vc.wp[0] = 0;
56628fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
56728fbf1d5SJoseph Myers 			} else {
56828fbf1d5SJoseph Myers 				SB0_e += (func == EVFSCTSF ? 31 : 32);
56928fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32,
57028fbf1d5SJoseph Myers 						(func == EVFSCTSF));
57128fbf1d5SJoseph Myers 			}
57228fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
57328fbf1d5SJoseph Myers 				vc.wp[1] = 0;
57428fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
57528fbf1d5SJoseph Myers 			} else {
57628fbf1d5SJoseph Myers 				SB1_e += (func == EVFSCTSF ? 31 : 32);
57728fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32,
57828fbf1d5SJoseph Myers 						(func == EVFSCTSF));
57928fbf1d5SJoseph Myers 			}
5806a800f36SLiu Yu 			goto update_regs;
5816a800f36SLiu Yu 
5826a800f36SLiu Yu 		case EVFSCTUI:
5836a800f36SLiu Yu 		case EVFSCTSI:
58428fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
58528fbf1d5SJoseph Myers 				vc.wp[0] = 0;
58628fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
58728fbf1d5SJoseph Myers 			} else {
58828fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32,
58928fbf1d5SJoseph Myers 						((func & 0x3) != 0));
59028fbf1d5SJoseph Myers 			}
59128fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
59228fbf1d5SJoseph Myers 				vc.wp[1] = 0;
59328fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
59428fbf1d5SJoseph Myers 			} else {
59528fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32,
59628fbf1d5SJoseph Myers 						((func & 0x3) != 0));
59728fbf1d5SJoseph Myers 			}
59828fbf1d5SJoseph Myers 			goto update_regs;
59928fbf1d5SJoseph Myers 
6006a800f36SLiu Yu 		case EVFSCTUIZ:
6016a800f36SLiu Yu 		case EVFSCTSIZ:
60228fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
60328fbf1d5SJoseph Myers 				vc.wp[0] = 0;
60428fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
6056a800f36SLiu Yu 			} else {
606afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[0], SB0, 32,
60728fbf1d5SJoseph Myers 						((func & 0x3) != 0));
60828fbf1d5SJoseph Myers 			}
60928fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
61028fbf1d5SJoseph Myers 				vc.wp[1] = 0;
61128fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
61228fbf1d5SJoseph Myers 			} else {
613afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[1], SB1, 32,
61428fbf1d5SJoseph Myers 						((func & 0x3) != 0));
61528fbf1d5SJoseph Myers 			}
6166a800f36SLiu Yu 			goto update_regs;
6176a800f36SLiu Yu 
6186a800f36SLiu Yu 		default:
6196a800f36SLiu Yu 			goto illegal;
6206a800f36SLiu Yu 		}
6216a800f36SLiu Yu 		break;
6226a800f36SLiu Yu 
6236a800f36SLiu Yu pack_vs:
624b430abc4SLiu Yu 		pr_debug("SR0: %ld %08lx %ld (%ld)\n",
625b430abc4SLiu Yu 				SR0_s, SR0_f, SR0_e, SR0_c);
626b430abc4SLiu Yu 		pr_debug("SR1: %ld %08lx %ld (%ld)\n",
627b430abc4SLiu Yu 				SR1_s, SR1_f, SR1_e, SR1_c);
628b430abc4SLiu Yu 
6296a800f36SLiu Yu 		FP_PACK_SP(vc.wp, SR0);
6306a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR1);
6316a800f36SLiu Yu 		goto update_regs;
6326a800f36SLiu Yu 
6336a800f36SLiu Yu cmp_vs:
6346a800f36SLiu Yu 		{
6356a800f36SLiu Yu 			int ch, cl;
6366a800f36SLiu Yu 
6376a800f36SLiu Yu 			FP_CMP_S(IR0, SA0, SB0, 3);
6386a800f36SLiu Yu 			FP_CMP_S(IR1, SA1, SB1, 3);
6396a800f36SLiu Yu 			if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
6406a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6416a800f36SLiu Yu 			if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
6426a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6436a800f36SLiu Yu 			ch = (IR0 == cmp) ? 1 : 0;
6446a800f36SLiu Yu 			cl = (IR1 == cmp) ? 1 : 0;
6456a800f36SLiu Yu 			IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
6466a800f36SLiu Yu 				((ch & cl) << 0);
6476a800f36SLiu Yu 			goto update_ccr;
6486a800f36SLiu Yu 		}
6496a800f36SLiu Yu 	}
6506a800f36SLiu Yu 	default:
6516a800f36SLiu Yu 		return -EINVAL;
6526a800f36SLiu Yu 	}
6536a800f36SLiu Yu 
6546a800f36SLiu Yu update_ccr:
6556a800f36SLiu Yu 	regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6566a800f36SLiu Yu 	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6576a800f36SLiu Yu 
6586a800f36SLiu Yu update_regs:
659640e9225SJoseph Myers 	/*
660640e9225SJoseph Myers 	 * If the "invalid" exception sticky bit was set by the
661640e9225SJoseph Myers 	 * processor for non-finite input, but was not set before the
662640e9225SJoseph Myers 	 * instruction being emulated, clear it.  Likewise for the
663640e9225SJoseph Myers 	 * "underflow" bit, which may have been set by the processor
664640e9225SJoseph Myers 	 * for exact underflow, not just inexact underflow when the
665640e9225SJoseph Myers 	 * flag should be set for IEEE 754 semantics.  Other sticky
666640e9225SJoseph Myers 	 * exceptions will only be set by the processor when they are
667640e9225SJoseph Myers 	 * correct according to IEEE 754 semantics, and we must not
668640e9225SJoseph Myers 	 * clear sticky bits that were already set before the emulated
669640e9225SJoseph Myers 	 * instruction as they represent the user-visible sticky
670640e9225SJoseph Myers 	 * exception status.  "inexact" traps to kernel are not
671640e9225SJoseph Myers 	 * required for IEEE semantics and are not enabled by default,
672640e9225SJoseph Myers 	 * so the "inexact" sticky bit may have been set by a previous
673640e9225SJoseph Myers 	 * instruction without the kernel being aware of it.
674640e9225SJoseph Myers 	 */
675640e9225SJoseph Myers 	__FPU_FPSCR
676640e9225SJoseph Myers 	  &= ~(FP_EX_INVALID | FP_EX_UNDERFLOW) | current->thread.spefscr_last;
6776a800f36SLiu Yu 	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
6786a800f36SLiu Yu 	mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
679640e9225SJoseph Myers 	current->thread.spefscr_last = __FPU_FPSCR;
6806a800f36SLiu Yu 
6816a800f36SLiu Yu 	current->thread.evr[fc] = vc.wp[0];
6826a800f36SLiu Yu 	regs->gpr[fc] = vc.wp[1];
6836a800f36SLiu Yu 
684b430abc4SLiu Yu 	pr_debug("ccr = %08lx\n", regs->ccr);
685b430abc4SLiu Yu 	pr_debug("cur exceptions = %08x spefscr = %08lx\n",
6866a800f36SLiu Yu 			FP_CUR_EXCEPTIONS, __FPU_FPSCR);
687b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
688b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
689b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
6906a800f36SLiu Yu 
69101c9cceeSJoseph Myers 	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE) {
69201c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_DIVZERO)
69301c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_DIV))
69401c9cceeSJoseph Myers 			return 1;
69501c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_OVERFLOW)
69601c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_OVF))
69701c9cceeSJoseph Myers 			return 1;
69801c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_UNDERFLOW)
69901c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_UND))
70001c9cceeSJoseph Myers 			return 1;
70101c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT)
70201c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_RES))
70301c9cceeSJoseph Myers 			return 1;
70401c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_INVALID)
70501c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_INV))
70601c9cceeSJoseph Myers 			return 1;
70701c9cceeSJoseph Myers 	}
7086a800f36SLiu Yu 	return 0;
7096a800f36SLiu Yu 
7106a800f36SLiu Yu illegal:
711ac6f1203SLiu Yu 	if (have_e500_cpu_a005_erratum) {
712ac6f1203SLiu Yu 		/* according to e500 cpu a005 erratum, reissue efp inst */
713*59dc5bfcSNicholas Piggin 		regs_add_return_ip(regs, -4);
714b430abc4SLiu Yu 		pr_debug("re-issue efp inst: %08lx\n", speinsn);
715ac6f1203SLiu Yu 		return 0;
716ac6f1203SLiu Yu 	}
717ac6f1203SLiu Yu 
7186a800f36SLiu Yu 	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
7196a800f36SLiu Yu 	return -ENOSYS;
7206a800f36SLiu Yu }
7216a800f36SLiu Yu 
7226a800f36SLiu Yu int speround_handler(struct pt_regs *regs)
7236a800f36SLiu Yu {
7246a800f36SLiu Yu 	union dw_union fgpr;
7256a800f36SLiu Yu 	int s_lo, s_hi;
72628414a6dSJoseph Myers 	int lo_inexact, hi_inexact;
72728fbf1d5SJoseph Myers 	int fp_result;
72828fbf1d5SJoseph Myers 	unsigned long speinsn, type, fb, fc, fptype, func;
7296a800f36SLiu Yu 
7306a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
7316a800f36SLiu Yu 		return -EFAULT;
7326a800f36SLiu Yu 	if ((speinsn >> 26) != 4)
7336a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
7346a800f36SLiu Yu 
73528fbf1d5SJoseph Myers 	func = speinsn & 0x7ff;
73628fbf1d5SJoseph Myers 	type = insn_type(func);
7376a800f36SLiu Yu 	if (type == XCR) return -ENOSYS;
7386a800f36SLiu Yu 
739d5755e6fSLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
740d5755e6fSLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
741d5755e6fSLiu Yu 
74228414a6dSJoseph Myers 	fptype = (speinsn >> 5) & 0x7;
74328414a6dSJoseph Myers 
744d5755e6fSLiu Yu 	/* No need to round if the result is exact */
74528414a6dSJoseph Myers 	lo_inexact = __FPU_FPSCR & (SPEFSCR_FG | SPEFSCR_FX);
74628414a6dSJoseph Myers 	hi_inexact = __FPU_FPSCR & (SPEFSCR_FGH | SPEFSCR_FXH);
74728414a6dSJoseph Myers 	if (!(lo_inexact || (hi_inexact && fptype == VCT)))
748d5755e6fSLiu Yu 		return 0;
749d5755e6fSLiu Yu 
7506a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
7516a800f36SLiu Yu 	s_lo = regs->gpr[fc] & SIGN_BIT_S;
7526a800f36SLiu Yu 	s_hi = current->thread.evr[fc] & SIGN_BIT_S;
7536a800f36SLiu Yu 	fgpr.wp[0] = current->thread.evr[fc];
7546a800f36SLiu Yu 	fgpr.wp[1] = regs->gpr[fc];
7556a800f36SLiu Yu 
75628fbf1d5SJoseph Myers 	fb = (speinsn >> 11) & 0x1f;
75728fbf1d5SJoseph Myers 	switch (func) {
75828fbf1d5SJoseph Myers 	case EFSCTUIZ:
75928fbf1d5SJoseph Myers 	case EFSCTSIZ:
76028fbf1d5SJoseph Myers 	case EVFSCTUIZ:
76128fbf1d5SJoseph Myers 	case EVFSCTSIZ:
76228fbf1d5SJoseph Myers 	case EFDCTUIDZ:
76328fbf1d5SJoseph Myers 	case EFDCTSIDZ:
76428fbf1d5SJoseph Myers 	case EFDCTUIZ:
76528fbf1d5SJoseph Myers 	case EFDCTSIZ:
76628fbf1d5SJoseph Myers 		/*
76728fbf1d5SJoseph Myers 		 * These instructions always round to zero,
76828fbf1d5SJoseph Myers 		 * independent of the rounding mode.
76928fbf1d5SJoseph Myers 		 */
77028fbf1d5SJoseph Myers 		return 0;
77128fbf1d5SJoseph Myers 
77228fbf1d5SJoseph Myers 	case EFSCTUI:
77328fbf1d5SJoseph Myers 	case EFSCTUF:
77428fbf1d5SJoseph Myers 	case EVFSCTUI:
77528fbf1d5SJoseph Myers 	case EVFSCTUF:
77628fbf1d5SJoseph Myers 	case EFDCTUI:
77728fbf1d5SJoseph Myers 	case EFDCTUF:
77828fbf1d5SJoseph Myers 		fp_result = 0;
77928fbf1d5SJoseph Myers 		s_lo = 0;
78028fbf1d5SJoseph Myers 		s_hi = 0;
78128fbf1d5SJoseph Myers 		break;
78228fbf1d5SJoseph Myers 
78328fbf1d5SJoseph Myers 	case EFSCTSI:
78428fbf1d5SJoseph Myers 	case EFSCTSF:
78528fbf1d5SJoseph Myers 		fp_result = 0;
78628fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
78728fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
78828fbf1d5SJoseph Myers 			s_lo = regs->gpr[fb] & SIGN_BIT_S;
78928fbf1d5SJoseph Myers 		break;
79028fbf1d5SJoseph Myers 
79128fbf1d5SJoseph Myers 	case EVFSCTSI:
79228fbf1d5SJoseph Myers 	case EVFSCTSF:
79328fbf1d5SJoseph Myers 		fp_result = 0;
79428fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
79528fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
79628fbf1d5SJoseph Myers 			s_lo = regs->gpr[fb] & SIGN_BIT_S;
79728fbf1d5SJoseph Myers 		if (fgpr.wp[0] == 0)
79828fbf1d5SJoseph Myers 			s_hi = current->thread.evr[fb] & SIGN_BIT_S;
79928fbf1d5SJoseph Myers 		break;
80028fbf1d5SJoseph Myers 
80128fbf1d5SJoseph Myers 	case EFDCTSI:
80228fbf1d5SJoseph Myers 	case EFDCTSF:
80328fbf1d5SJoseph Myers 		fp_result = 0;
80428fbf1d5SJoseph Myers 		s_hi = s_lo;
80528fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
80628fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
80728fbf1d5SJoseph Myers 			s_hi = current->thread.evr[fb] & SIGN_BIT_S;
80828fbf1d5SJoseph Myers 		break;
80928fbf1d5SJoseph Myers 
81028fbf1d5SJoseph Myers 	default:
81128fbf1d5SJoseph Myers 		fp_result = 1;
81228fbf1d5SJoseph Myers 		break;
81328fbf1d5SJoseph Myers 	}
81428fbf1d5SJoseph Myers 
815d5755e6fSLiu Yu 	pr_debug("round fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
8166a800f36SLiu Yu 
81728414a6dSJoseph Myers 	switch (fptype) {
8186a800f36SLiu Yu 	/* Since SPE instructions on E500 core can handle round to nearest
8196a800f36SLiu Yu 	 * and round toward zero with IEEE-754 complied, we just need
8206a800f36SLiu Yu 	 * to handle round toward +Inf and round toward -Inf by software.
8216a800f36SLiu Yu 	 */
8226a800f36SLiu Yu 	case SPFP:
8236a800f36SLiu Yu 		if ((FP_ROUNDMODE) == FP_RND_PINF) {
8246a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
8256a800f36SLiu Yu 		} else { /* round to -Inf */
82628fbf1d5SJoseph Myers 			if (s_lo) {
82728fbf1d5SJoseph Myers 				if (fp_result)
82828fbf1d5SJoseph Myers 					fgpr.wp[1]++; /* Z < 0, choose Z2 */
82928fbf1d5SJoseph Myers 				else
83028fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z < 0, choose Z2 */
83128fbf1d5SJoseph Myers 			}
8326a800f36SLiu Yu 		}
8336a800f36SLiu Yu 		break;
8346a800f36SLiu Yu 
8356a800f36SLiu Yu 	case DPFP:
8366a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
83728fbf1d5SJoseph Myers 			if (!s_hi) {
83828fbf1d5SJoseph Myers 				if (fp_result)
83928fbf1d5SJoseph Myers 					fgpr.dp[0]++; /* Z > 0, choose Z1 */
84028fbf1d5SJoseph Myers 				else
84128fbf1d5SJoseph Myers 					fgpr.wp[1]++; /* Z > 0, choose Z1 */
84228fbf1d5SJoseph Myers 			}
8436a800f36SLiu Yu 		} else { /* round to -Inf */
84428fbf1d5SJoseph Myers 			if (s_hi) {
84528fbf1d5SJoseph Myers 				if (fp_result)
84628fbf1d5SJoseph Myers 					fgpr.dp[0]++; /* Z < 0, choose Z2 */
84728fbf1d5SJoseph Myers 				else
84828fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z < 0, choose Z2 */
84928fbf1d5SJoseph Myers 			}
8506a800f36SLiu Yu 		}
8516a800f36SLiu Yu 		break;
8526a800f36SLiu Yu 
8536a800f36SLiu Yu 	case VCT:
8546a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
85528414a6dSJoseph Myers 			if (lo_inexact && !s_lo)
85628414a6dSJoseph Myers 				fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
85728414a6dSJoseph Myers 			if (hi_inexact && !s_hi)
85828414a6dSJoseph Myers 				fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
8596a800f36SLiu Yu 		} else { /* round to -Inf */
86028fbf1d5SJoseph Myers 			if (lo_inexact && s_lo) {
86128fbf1d5SJoseph Myers 				if (fp_result)
86228414a6dSJoseph Myers 					fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
86328fbf1d5SJoseph Myers 				else
86428fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z_low < 0, choose Z2 */
86528fbf1d5SJoseph Myers 			}
86628fbf1d5SJoseph Myers 			if (hi_inexact && s_hi) {
86728fbf1d5SJoseph Myers 				if (fp_result)
86828414a6dSJoseph Myers 					fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
86928fbf1d5SJoseph Myers 				else
87028fbf1d5SJoseph Myers 					fgpr.wp[0]--; /* Z_high < 0, choose Z2 */
87128fbf1d5SJoseph Myers 			}
8726a800f36SLiu Yu 		}
8736a800f36SLiu Yu 		break;
8746a800f36SLiu Yu 
8756a800f36SLiu Yu 	default:
8766a800f36SLiu Yu 		return -EINVAL;
8776a800f36SLiu Yu 	}
8786a800f36SLiu Yu 
8796a800f36SLiu Yu 	current->thread.evr[fc] = fgpr.wp[0];
8806a800f36SLiu Yu 	regs->gpr[fc] = fgpr.wp[1];
8816a800f36SLiu Yu 
882d5755e6fSLiu Yu 	pr_debug("  to fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
883d5755e6fSLiu Yu 
88401c9cceeSJoseph Myers 	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE)
88501c9cceeSJoseph Myers 		return (current->thread.fpexc_mode & PR_FP_EXC_RES) ? 1 : 0;
8866a800f36SLiu Yu 	return 0;
8876a800f36SLiu Yu }
888ac6f1203SLiu Yu 
889ac6f1203SLiu Yu int __init spe_mathemu_init(void)
890ac6f1203SLiu Yu {
891ac6f1203SLiu Yu 	u32 pvr, maj, min;
892ac6f1203SLiu Yu 
893ac6f1203SLiu Yu 	pvr = mfspr(SPRN_PVR);
894ac6f1203SLiu Yu 
895ac6f1203SLiu Yu 	if ((PVR_VER(pvr) == PVR_VER_E500V1) ||
896ac6f1203SLiu Yu 	    (PVR_VER(pvr) == PVR_VER_E500V2)) {
897ac6f1203SLiu Yu 		maj = PVR_MAJ(pvr);
898ac6f1203SLiu Yu 		min = PVR_MIN(pvr);
899ac6f1203SLiu Yu 
900ac6f1203SLiu Yu 		/*
901ac6f1203SLiu Yu 		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1
902ac6f1203SLiu Yu 		 * need cpu a005 errata workaround
903ac6f1203SLiu Yu 		 */
904ac6f1203SLiu Yu 		switch (maj) {
905ac6f1203SLiu Yu 		case 1:
906ac6f1203SLiu Yu 			if (min < 1)
907ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
908ac6f1203SLiu Yu 			break;
909ac6f1203SLiu Yu 		case 2:
910ac6f1203SLiu Yu 			if (min < 3)
911ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
912ac6f1203SLiu Yu 			break;
913ac6f1203SLiu Yu 		case 3:
914ac6f1203SLiu Yu 		case 4:
915ac6f1203SLiu Yu 		case 5:
916ac6f1203SLiu Yu 			if (min < 1)
917ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
918ac6f1203SLiu Yu 			break;
919ac6f1203SLiu Yu 		default:
920ac6f1203SLiu Yu 			break;
921ac6f1203SLiu Yu 		}
922ac6f1203SLiu Yu 	}
923ac6f1203SLiu Yu 
924ac6f1203SLiu Yu 	return 0;
925ac6f1203SLiu Yu }
926ac6f1203SLiu Yu 
927ac6f1203SLiu Yu module_init(spe_mathemu_init);
928