xref: /openbmc/linux/arch/powerpc/math-emu/math_efp.c (revision 78c73c80)
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>
20cfe0d370SNathan Chancellor #include <linux/module.h>
216a800f36SLiu Yu 
227c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
236a800f36SLiu Yu #include <asm/reg.h>
246a800f36SLiu Yu 
256a800f36SLiu Yu #define FP_EX_BOOKE_E500_SPE
266a800f36SLiu Yu #include <asm/sfp-machine.h>
276a800f36SLiu Yu 
286a800f36SLiu Yu #include <math-emu/soft-fp.h>
296a800f36SLiu Yu #include <math-emu/single.h>
306a800f36SLiu Yu #include <math-emu/double.h>
316a800f36SLiu Yu 
326a800f36SLiu Yu #define EFAPU		0x4
336a800f36SLiu Yu 
346a800f36SLiu Yu #define VCT		0x4
356a800f36SLiu Yu #define SPFP		0x6
366a800f36SLiu Yu #define DPFP		0x7
376a800f36SLiu Yu 
386a800f36SLiu Yu #define EFSADD		0x2c0
396a800f36SLiu Yu #define EFSSUB		0x2c1
406a800f36SLiu Yu #define EFSABS		0x2c4
416a800f36SLiu Yu #define EFSNABS		0x2c5
426a800f36SLiu Yu #define EFSNEG		0x2c6
436a800f36SLiu Yu #define EFSMUL		0x2c8
446a800f36SLiu Yu #define EFSDIV		0x2c9
456a800f36SLiu Yu #define EFSCMPGT	0x2cc
466a800f36SLiu Yu #define EFSCMPLT	0x2cd
476a800f36SLiu Yu #define EFSCMPEQ	0x2ce
486a800f36SLiu Yu #define EFSCFD		0x2cf
496a800f36SLiu Yu #define EFSCFSI		0x2d1
506a800f36SLiu Yu #define EFSCTUI		0x2d4
516a800f36SLiu Yu #define EFSCTSI		0x2d5
526a800f36SLiu Yu #define EFSCTUF		0x2d6
536a800f36SLiu Yu #define EFSCTSF		0x2d7
546a800f36SLiu Yu #define EFSCTUIZ	0x2d8
556a800f36SLiu Yu #define EFSCTSIZ	0x2da
566a800f36SLiu Yu 
576a800f36SLiu Yu #define EVFSADD		0x280
586a800f36SLiu Yu #define EVFSSUB		0x281
596a800f36SLiu Yu #define EVFSABS		0x284
606a800f36SLiu Yu #define EVFSNABS	0x285
616a800f36SLiu Yu #define EVFSNEG		0x286
626a800f36SLiu Yu #define EVFSMUL		0x288
636a800f36SLiu Yu #define EVFSDIV		0x289
646a800f36SLiu Yu #define EVFSCMPGT	0x28c
656a800f36SLiu Yu #define EVFSCMPLT	0x28d
666a800f36SLiu Yu #define EVFSCMPEQ	0x28e
676a800f36SLiu Yu #define EVFSCTUI	0x294
686a800f36SLiu Yu #define EVFSCTSI	0x295
696a800f36SLiu Yu #define EVFSCTUF	0x296
706a800f36SLiu Yu #define EVFSCTSF	0x297
716a800f36SLiu Yu #define EVFSCTUIZ	0x298
726a800f36SLiu Yu #define EVFSCTSIZ	0x29a
736a800f36SLiu Yu 
746a800f36SLiu Yu #define EFDADD		0x2e0
756a800f36SLiu Yu #define EFDSUB		0x2e1
766a800f36SLiu Yu #define EFDABS		0x2e4
776a800f36SLiu Yu #define EFDNABS		0x2e5
786a800f36SLiu Yu #define EFDNEG		0x2e6
796a800f36SLiu Yu #define EFDMUL		0x2e8
806a800f36SLiu Yu #define EFDDIV		0x2e9
816a800f36SLiu Yu #define EFDCTUIDZ	0x2ea
826a800f36SLiu Yu #define EFDCTSIDZ	0x2eb
836a800f36SLiu Yu #define EFDCMPGT	0x2ec
846a800f36SLiu Yu #define EFDCMPLT	0x2ed
856a800f36SLiu Yu #define EFDCMPEQ	0x2ee
866a800f36SLiu Yu #define EFDCFS		0x2ef
876a800f36SLiu Yu #define EFDCTUI		0x2f4
886a800f36SLiu Yu #define EFDCTSI		0x2f5
896a800f36SLiu Yu #define EFDCTUF		0x2f6
906a800f36SLiu Yu #define EFDCTSF		0x2f7
916a800f36SLiu Yu #define EFDCTUIZ	0x2f8
926a800f36SLiu Yu #define EFDCTSIZ	0x2fa
936a800f36SLiu Yu 
946a800f36SLiu Yu #define AB	2
956a800f36SLiu Yu #define XA	3
966a800f36SLiu Yu #define XB	4
976a800f36SLiu Yu #define XCR	5
986a800f36SLiu Yu #define NOTYPE	0
996a800f36SLiu Yu 
1006a800f36SLiu Yu #define SIGN_BIT_S	(1UL << 31)
1016a800f36SLiu Yu #define SIGN_BIT_D	(1ULL << 63)
1026a800f36SLiu Yu #define FP_EX_MASK	(FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \
1036a800f36SLiu Yu 			FP_EX_UNDERFLOW | FP_EX_OVERFLOW)
1046a800f36SLiu Yu 
105ac6f1203SLiu Yu static int have_e500_cpu_a005_erratum;
106ac6f1203SLiu Yu 
1076a800f36SLiu Yu union dw_union {
1086a800f36SLiu Yu 	u64 dp[1];
1096a800f36SLiu Yu 	u32 wp[2];
1106a800f36SLiu Yu };
1116a800f36SLiu Yu 
insn_type(unsigned long speinsn)1126a800f36SLiu Yu static unsigned long insn_type(unsigned long speinsn)
1136a800f36SLiu Yu {
1146a800f36SLiu Yu 	unsigned long ret = NOTYPE;
1156a800f36SLiu Yu 
1166a800f36SLiu Yu 	switch (speinsn & 0x7ff) {
1176a800f36SLiu Yu 	case EFSABS:	ret = XA;	break;
1186a800f36SLiu Yu 	case EFSADD:	ret = AB;	break;
1196a800f36SLiu Yu 	case EFSCFD:	ret = XB;	break;
1206a800f36SLiu Yu 	case EFSCMPEQ:	ret = XCR;	break;
1216a800f36SLiu Yu 	case EFSCMPGT:	ret = XCR;	break;
1226a800f36SLiu Yu 	case EFSCMPLT:	ret = XCR;	break;
1236a800f36SLiu Yu 	case EFSCTSF:	ret = XB;	break;
1246a800f36SLiu Yu 	case EFSCTSI:	ret = XB;	break;
1256a800f36SLiu Yu 	case EFSCTSIZ:	ret = XB;	break;
1266a800f36SLiu Yu 	case EFSCTUF:	ret = XB;	break;
1276a800f36SLiu Yu 	case EFSCTUI:	ret = XB;	break;
1286a800f36SLiu Yu 	case EFSCTUIZ:	ret = XB;	break;
1296a800f36SLiu Yu 	case EFSDIV:	ret = AB;	break;
1306a800f36SLiu Yu 	case EFSMUL:	ret = AB;	break;
1316a800f36SLiu Yu 	case EFSNABS:	ret = XA;	break;
1326a800f36SLiu Yu 	case EFSNEG:	ret = XA;	break;
1336a800f36SLiu Yu 	case EFSSUB:	ret = AB;	break;
1346a800f36SLiu Yu 	case EFSCFSI:	ret = XB;	break;
1356a800f36SLiu Yu 
1366a800f36SLiu Yu 	case EVFSABS:	ret = XA;	break;
1376a800f36SLiu Yu 	case EVFSADD:	ret = AB;	break;
1386a800f36SLiu Yu 	case EVFSCMPEQ:	ret = XCR;	break;
1396a800f36SLiu Yu 	case EVFSCMPGT:	ret = XCR;	break;
1406a800f36SLiu Yu 	case EVFSCMPLT:	ret = XCR;	break;
1416a800f36SLiu Yu 	case EVFSCTSF:	ret = XB;	break;
1426a800f36SLiu Yu 	case EVFSCTSI:	ret = XB;	break;
1436a800f36SLiu Yu 	case EVFSCTSIZ:	ret = XB;	break;
1446a800f36SLiu Yu 	case EVFSCTUF:	ret = XB;	break;
1456a800f36SLiu Yu 	case EVFSCTUI:	ret = XB;	break;
1466a800f36SLiu Yu 	case EVFSCTUIZ:	ret = XB;	break;
1476a800f36SLiu Yu 	case EVFSDIV:	ret = AB;	break;
1486a800f36SLiu Yu 	case EVFSMUL:	ret = AB;	break;
1496a800f36SLiu Yu 	case EVFSNABS:	ret = XA;	break;
1506a800f36SLiu Yu 	case EVFSNEG:	ret = XA;	break;
1516a800f36SLiu Yu 	case EVFSSUB:	ret = AB;	break;
1526a800f36SLiu Yu 
1536a800f36SLiu Yu 	case EFDABS:	ret = XA;	break;
1546a800f36SLiu Yu 	case EFDADD:	ret = AB;	break;
1556a800f36SLiu Yu 	case EFDCFS:	ret = XB;	break;
1566a800f36SLiu Yu 	case EFDCMPEQ:	ret = XCR;	break;
1576a800f36SLiu Yu 	case EFDCMPGT:	ret = XCR;	break;
1586a800f36SLiu Yu 	case EFDCMPLT:	ret = XCR;	break;
1596a800f36SLiu Yu 	case EFDCTSF:	ret = XB;	break;
1606a800f36SLiu Yu 	case EFDCTSI:	ret = XB;	break;
1616a800f36SLiu Yu 	case EFDCTSIDZ:	ret = XB;	break;
1626a800f36SLiu Yu 	case EFDCTSIZ:	ret = XB;	break;
1636a800f36SLiu Yu 	case EFDCTUF:	ret = XB;	break;
1646a800f36SLiu Yu 	case EFDCTUI:	ret = XB;	break;
1656a800f36SLiu Yu 	case EFDCTUIDZ:	ret = XB;	break;
1666a800f36SLiu Yu 	case EFDCTUIZ:	ret = XB;	break;
1676a800f36SLiu Yu 	case EFDDIV:	ret = AB;	break;
1686a800f36SLiu Yu 	case EFDMUL:	ret = AB;	break;
1696a800f36SLiu Yu 	case EFDNABS:	ret = XA;	break;
1706a800f36SLiu Yu 	case EFDNEG:	ret = XA;	break;
1716a800f36SLiu Yu 	case EFDSUB:	ret = AB;	break;
1726a800f36SLiu Yu 	}
1736a800f36SLiu Yu 
1746a800f36SLiu Yu 	return ret;
1756a800f36SLiu Yu }
1766a800f36SLiu Yu 
do_spe_mathemu(struct pt_regs * regs)1776a800f36SLiu Yu int do_spe_mathemu(struct pt_regs *regs)
1786a800f36SLiu Yu {
1796a800f36SLiu Yu 	FP_DECL_EX;
1806a800f36SLiu Yu 	int IR, cmp;
1816a800f36SLiu Yu 
1826a800f36SLiu Yu 	unsigned long type, func, fc, fa, fb, src, speinsn;
1836a800f36SLiu Yu 	union dw_union vc, va, vb;
1846a800f36SLiu Yu 
1856a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
1866a800f36SLiu Yu 		return -EFAULT;
1876a800f36SLiu Yu 	if ((speinsn >> 26) != EFAPU)
1886a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
1896a800f36SLiu Yu 
1906a800f36SLiu Yu 	type = insn_type(speinsn);
1916a800f36SLiu Yu 	if (type == NOTYPE)
19209af52f7SLiu Yu 		goto illegal;
1936a800f36SLiu Yu 
1946a800f36SLiu Yu 	func = speinsn & 0x7ff;
1956a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
1966a800f36SLiu Yu 	fa = (speinsn >> 16) & 0x1f;
1976a800f36SLiu Yu 	fb = (speinsn >> 11) & 0x1f;
1986a800f36SLiu Yu 	src = (speinsn >> 5) & 0x7;
1996a800f36SLiu Yu 
2006a800f36SLiu Yu 	vc.wp[0] = current->thread.evr[fc];
2016a800f36SLiu Yu 	vc.wp[1] = regs->gpr[fc];
2026a800f36SLiu Yu 	va.wp[0] = current->thread.evr[fa];
2036a800f36SLiu Yu 	va.wp[1] = regs->gpr[fa];
2046a800f36SLiu Yu 	vb.wp[0] = current->thread.evr[fb];
2056a800f36SLiu Yu 	vb.wp[1] = regs->gpr[fb];
2066a800f36SLiu Yu 
2076a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
2086a800f36SLiu Yu 
209b430abc4SLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
210b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
211b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
212b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
2136a800f36SLiu Yu 
2146a800f36SLiu Yu 	switch (src) {
2156a800f36SLiu Yu 	case SPFP: {
2166a800f36SLiu Yu 		FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
2176a800f36SLiu Yu 
2186a800f36SLiu Yu 		switch (type) {
2196a800f36SLiu Yu 		case AB:
2206a800f36SLiu Yu 		case XCR:
2216a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2227245fc5bSChristophe Leroy 			fallthrough;
2236a800f36SLiu Yu 		case XB:
2246a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
2256a800f36SLiu Yu 			break;
2266a800f36SLiu Yu 		case XA:
2276a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2286a800f36SLiu Yu 			break;
2296a800f36SLiu Yu 		}
2306a800f36SLiu Yu 
2317245fc5bSChristophe Leroy 		pr_debug("SA: %d %08x %d (%d)\n", SA_s, SA_f, SA_e, SA_c);
2327245fc5bSChristophe Leroy 		pr_debug("SB: %d %08x %d (%d)\n", SB_s, SB_f, SB_e, SB_c);
2336a800f36SLiu Yu 
2346a800f36SLiu Yu 		switch (func) {
2356a800f36SLiu Yu 		case EFSABS:
2366a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
2376a800f36SLiu Yu 			goto update_regs;
2386a800f36SLiu Yu 
2396a800f36SLiu Yu 		case EFSNABS:
2406a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
2416a800f36SLiu Yu 			goto update_regs;
2426a800f36SLiu Yu 
2436a800f36SLiu Yu 		case EFSNEG:
2446a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
2456a800f36SLiu Yu 			goto update_regs;
2466a800f36SLiu Yu 
2476a800f36SLiu Yu 		case EFSADD:
2486a800f36SLiu Yu 			FP_ADD_S(SR, SA, SB);
2496a800f36SLiu Yu 			goto pack_s;
2506a800f36SLiu Yu 
2516a800f36SLiu Yu 		case EFSSUB:
2526a800f36SLiu Yu 			FP_SUB_S(SR, SA, SB);
2536a800f36SLiu Yu 			goto pack_s;
2546a800f36SLiu Yu 
2556a800f36SLiu Yu 		case EFSMUL:
2566a800f36SLiu Yu 			FP_MUL_S(SR, SA, SB);
2576a800f36SLiu Yu 			goto pack_s;
2586a800f36SLiu Yu 
2596a800f36SLiu Yu 		case EFSDIV:
2606a800f36SLiu Yu 			FP_DIV_S(SR, SA, SB);
2616a800f36SLiu Yu 			goto pack_s;
2626a800f36SLiu Yu 
2636a800f36SLiu Yu 		case EFSCMPEQ:
2646a800f36SLiu Yu 			cmp = 0;
2656a800f36SLiu Yu 			goto cmp_s;
2666a800f36SLiu Yu 
2676a800f36SLiu Yu 		case EFSCMPGT:
2686a800f36SLiu Yu 			cmp = 1;
2696a800f36SLiu Yu 			goto cmp_s;
2706a800f36SLiu Yu 
2716a800f36SLiu Yu 		case EFSCMPLT:
2726a800f36SLiu Yu 			cmp = -1;
2736a800f36SLiu Yu 			goto cmp_s;
2746a800f36SLiu Yu 
2756a800f36SLiu Yu 		case EFSCTSF:
2766a800f36SLiu Yu 		case EFSCTUF:
27728fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
27828fbf1d5SJoseph Myers 				vc.wp[1] = 0;
27928fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
28028fbf1d5SJoseph Myers 			} else {
28128fbf1d5SJoseph Myers 				SB_e += (func == EFSCTSF ? 31 : 32);
28228fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32,
2837245fc5bSChristophe Leroy 						(func == EFSCTSF) ? 1 : 0);
2846a800f36SLiu Yu 			}
2856a800f36SLiu Yu 			goto update_regs;
2866a800f36SLiu Yu 
2876a800f36SLiu Yu 		case EFSCFD: {
2886a800f36SLiu Yu 			FP_DECL_D(DB);
2896a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
2906a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
291b430abc4SLiu Yu 
2927245fc5bSChristophe Leroy 			pr_debug("DB: %d %08x %08x %d (%d)\n",
2936a800f36SLiu Yu 					DB_s, DB_f1, DB_f0, DB_e, DB_c);
294b430abc4SLiu Yu 
2956a800f36SLiu Yu 			FP_CONV(S, D, 1, 2, SR, DB);
2966a800f36SLiu Yu 			goto pack_s;
2976a800f36SLiu Yu 		}
2986a800f36SLiu Yu 
2996a800f36SLiu Yu 		case EFSCTSI:
3006a800f36SLiu Yu 		case EFSCTUI:
30128fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
30228fbf1d5SJoseph Myers 				vc.wp[1] = 0;
30328fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
3046a800f36SLiu Yu 			} else {
30528fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB, 32,
3067245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
3076a800f36SLiu Yu 			}
30828fbf1d5SJoseph Myers 			goto update_regs;
30928fbf1d5SJoseph Myers 
31028fbf1d5SJoseph Myers 		case EFSCTSIZ:
31128fbf1d5SJoseph Myers 		case EFSCTUIZ:
31228fbf1d5SJoseph Myers 			if (SB_c == FP_CLS_NAN) {
31328fbf1d5SJoseph Myers 				vc.wp[1] = 0;
31428fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
31528fbf1d5SJoseph Myers 			} else {
316afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[1], SB, 32,
3177245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
31828fbf1d5SJoseph Myers 			}
3196a800f36SLiu Yu 			goto update_regs;
3206a800f36SLiu Yu 
3216a800f36SLiu Yu 		default:
3226a800f36SLiu Yu 			goto illegal;
3236a800f36SLiu Yu 		}
3246a800f36SLiu Yu 		break;
3256a800f36SLiu Yu 
3266a800f36SLiu Yu pack_s:
3277245fc5bSChristophe Leroy 		pr_debug("SR: %d %08x %d (%d)\n", SR_s, SR_f, SR_e, SR_c);
328b430abc4SLiu Yu 
3296a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR);
3306a800f36SLiu Yu 		goto update_regs;
3316a800f36SLiu Yu 
3326a800f36SLiu Yu cmp_s:
3336a800f36SLiu Yu 		FP_CMP_S(IR, SA, SB, 3);
3346a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB)))
3356a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
3366a800f36SLiu Yu 		if (IR == cmp) {
3376a800f36SLiu Yu 			IR = 0x4;
3386a800f36SLiu Yu 		} else {
3396a800f36SLiu Yu 			IR = 0;
3406a800f36SLiu Yu 		}
3416a800f36SLiu Yu 		goto update_ccr;
3426a800f36SLiu Yu 	}
3436a800f36SLiu Yu 
3446a800f36SLiu Yu 	case DPFP: {
3456a800f36SLiu Yu 		FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
3466a800f36SLiu Yu 
3476a800f36SLiu Yu 		switch (type) {
3486a800f36SLiu Yu 		case AB:
3496a800f36SLiu Yu 		case XCR:
3506a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3517245fc5bSChristophe Leroy 			fallthrough;
3526a800f36SLiu Yu 		case XB:
3536a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3546a800f36SLiu Yu 			break;
3556a800f36SLiu Yu 		case XA:
3566a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3576a800f36SLiu Yu 			break;
3586a800f36SLiu Yu 		}
3596a800f36SLiu Yu 
3607245fc5bSChristophe Leroy 		pr_debug("DA: %d %08x %08x %d (%d)\n",
3616a800f36SLiu Yu 				DA_s, DA_f1, DA_f0, DA_e, DA_c);
3627245fc5bSChristophe Leroy 		pr_debug("DB: %d %08x %08x %d (%d)\n",
3636a800f36SLiu Yu 				DB_s, DB_f1, DB_f0, DB_e, DB_c);
3646a800f36SLiu Yu 
3656a800f36SLiu Yu 		switch (func) {
3666a800f36SLiu Yu 		case EFDABS:
3676a800f36SLiu Yu 			vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
3686a800f36SLiu Yu 			goto update_regs;
3696a800f36SLiu Yu 
3706a800f36SLiu Yu 		case EFDNABS:
3716a800f36SLiu Yu 			vc.dp[0] = va.dp[0] | SIGN_BIT_D;
3726a800f36SLiu Yu 			goto update_regs;
3736a800f36SLiu Yu 
3746a800f36SLiu Yu 		case EFDNEG:
3756a800f36SLiu Yu 			vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
3766a800f36SLiu Yu 			goto update_regs;
3776a800f36SLiu Yu 
3786a800f36SLiu Yu 		case EFDADD:
3796a800f36SLiu Yu 			FP_ADD_D(DR, DA, DB);
3806a800f36SLiu Yu 			goto pack_d;
3816a800f36SLiu Yu 
3826a800f36SLiu Yu 		case EFDSUB:
3836a800f36SLiu Yu 			FP_SUB_D(DR, DA, DB);
3846a800f36SLiu Yu 			goto pack_d;
3856a800f36SLiu Yu 
3866a800f36SLiu Yu 		case EFDMUL:
3876a800f36SLiu Yu 			FP_MUL_D(DR, DA, DB);
3886a800f36SLiu Yu 			goto pack_d;
3896a800f36SLiu Yu 
3906a800f36SLiu Yu 		case EFDDIV:
3916a800f36SLiu Yu 			FP_DIV_D(DR, DA, DB);
3926a800f36SLiu Yu 			goto pack_d;
3936a800f36SLiu Yu 
3946a800f36SLiu Yu 		case EFDCMPEQ:
3956a800f36SLiu Yu 			cmp = 0;
3966a800f36SLiu Yu 			goto cmp_d;
3976a800f36SLiu Yu 
3986a800f36SLiu Yu 		case EFDCMPGT:
3996a800f36SLiu Yu 			cmp = 1;
4006a800f36SLiu Yu 			goto cmp_d;
4016a800f36SLiu Yu 
4026a800f36SLiu Yu 		case EFDCMPLT:
4036a800f36SLiu Yu 			cmp = -1;
4046a800f36SLiu Yu 			goto cmp_d;
4056a800f36SLiu Yu 
4066a800f36SLiu Yu 		case EFDCTSF:
4076a800f36SLiu Yu 		case EFDCTUF:
40828fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
40928fbf1d5SJoseph Myers 				vc.wp[1] = 0;
41028fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
41128fbf1d5SJoseph Myers 			} else {
41228fbf1d5SJoseph Myers 				DB_e += (func == EFDCTSF ? 31 : 32);
41328fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32,
4147245fc5bSChristophe Leroy 						(func == EFDCTSF) ? 1 : 0);
4156a800f36SLiu Yu 			}
4166a800f36SLiu Yu 			goto update_regs;
4176a800f36SLiu Yu 
4186a800f36SLiu Yu 		case EFDCFS: {
4196a800f36SLiu Yu 			FP_DECL_S(SB);
4206a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
4216a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
422b430abc4SLiu Yu 
4237245fc5bSChristophe Leroy 			pr_debug("SB: %d %08x %d (%d)\n",
4246a800f36SLiu Yu 					SB_s, SB_f, SB_e, SB_c);
425b430abc4SLiu Yu 
4266a800f36SLiu Yu 			FP_CONV(D, S, 2, 1, DR, SB);
4276a800f36SLiu Yu 			goto pack_d;
4286a800f36SLiu Yu 		}
4296a800f36SLiu Yu 
4306a800f36SLiu Yu 		case EFDCTUIDZ:
4316a800f36SLiu Yu 		case EFDCTSIDZ:
43228fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
43328fbf1d5SJoseph Myers 				vc.dp[0] = 0;
43428fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
43528fbf1d5SJoseph Myers 			} else {
43628fbf1d5SJoseph Myers 				FP_TO_INT_D(vc.dp[0], DB, 64,
4377245fc5bSChristophe Leroy 						((func & 0x1) == 0) ? 1 : 0);
43828fbf1d5SJoseph Myers 			}
4396a800f36SLiu Yu 			goto update_regs;
4406a800f36SLiu Yu 
4416a800f36SLiu Yu 		case EFDCTUI:
4426a800f36SLiu Yu 		case EFDCTSI:
44328fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
44428fbf1d5SJoseph Myers 				vc.wp[1] = 0;
44528fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
44628fbf1d5SJoseph Myers 			} else {
44728fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_D(vc.wp[1], DB, 32,
4487245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
44928fbf1d5SJoseph Myers 			}
45028fbf1d5SJoseph Myers 			goto update_regs;
45128fbf1d5SJoseph Myers 
4526a800f36SLiu Yu 		case EFDCTUIZ:
4536a800f36SLiu Yu 		case EFDCTSIZ:
45428fbf1d5SJoseph Myers 			if (DB_c == FP_CLS_NAN) {
45528fbf1d5SJoseph Myers 				vc.wp[1] = 0;
45628fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
4576a800f36SLiu Yu 			} else {
458afc0a07dSShan Hai 				FP_TO_INT_D(vc.wp[1], DB, 32,
4597245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
46028fbf1d5SJoseph Myers 			}
4616a800f36SLiu Yu 			goto update_regs;
4626a800f36SLiu Yu 
4636a800f36SLiu Yu 		default:
4646a800f36SLiu Yu 			goto illegal;
4656a800f36SLiu Yu 		}
4666a800f36SLiu Yu 		break;
4676a800f36SLiu Yu 
4686a800f36SLiu Yu pack_d:
4697245fc5bSChristophe Leroy 		pr_debug("DR: %d %08x %08x %d (%d)\n",
4706a800f36SLiu Yu 				DR_s, DR_f1, DR_f0, DR_e, DR_c);
471b430abc4SLiu Yu 
4726a800f36SLiu Yu 		FP_PACK_DP(vc.dp, DR);
4736a800f36SLiu Yu 		goto update_regs;
4746a800f36SLiu Yu 
4756a800f36SLiu Yu cmp_d:
4766a800f36SLiu Yu 		FP_CMP_D(IR, DA, DB, 3);
4776a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
4786a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
4796a800f36SLiu Yu 		if (IR == cmp) {
4806a800f36SLiu Yu 			IR = 0x4;
4816a800f36SLiu Yu 		} else {
4826a800f36SLiu Yu 			IR = 0;
4836a800f36SLiu Yu 		}
4846a800f36SLiu Yu 		goto update_ccr;
4856a800f36SLiu Yu 
4866a800f36SLiu Yu 	}
4876a800f36SLiu Yu 
4886a800f36SLiu Yu 	case VCT: {
4896a800f36SLiu Yu 		FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
4906a800f36SLiu Yu 		FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
4916a800f36SLiu Yu 		int IR0, IR1;
4926a800f36SLiu Yu 
4936a800f36SLiu Yu 		switch (type) {
4946a800f36SLiu Yu 		case AB:
4956a800f36SLiu Yu 		case XCR:
4966a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
4976a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
4987245fc5bSChristophe Leroy 			fallthrough;
4996a800f36SLiu Yu 		case XB:
5006a800f36SLiu Yu 			FP_UNPACK_SP(SB0, vb.wp);
5016a800f36SLiu Yu 			FP_UNPACK_SP(SB1, vb.wp + 1);
5026a800f36SLiu Yu 			break;
5036a800f36SLiu Yu 		case XA:
5046a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
5056a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
5066a800f36SLiu Yu 			break;
5076a800f36SLiu Yu 		}
5086a800f36SLiu Yu 
5097245fc5bSChristophe Leroy 		pr_debug("SA0: %d %08x %d (%d)\n",
510b430abc4SLiu Yu 				SA0_s, SA0_f, SA0_e, SA0_c);
5117245fc5bSChristophe Leroy 		pr_debug("SA1: %d %08x %d (%d)\n",
512b430abc4SLiu Yu 				SA1_s, SA1_f, SA1_e, SA1_c);
5137245fc5bSChristophe Leroy 		pr_debug("SB0: %d %08x %d (%d)\n",
514b430abc4SLiu Yu 				SB0_s, SB0_f, SB0_e, SB0_c);
5157245fc5bSChristophe Leroy 		pr_debug("SB1: %d %08x %d (%d)\n",
516b430abc4SLiu Yu 				SB1_s, SB1_f, SB1_e, SB1_c);
5176a800f36SLiu Yu 
5186a800f36SLiu Yu 		switch (func) {
5196a800f36SLiu Yu 		case EVFSABS:
5206a800f36SLiu Yu 			vc.wp[0] = va.wp[0] & ~SIGN_BIT_S;
5216a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
5226a800f36SLiu Yu 			goto update_regs;
5236a800f36SLiu Yu 
5246a800f36SLiu Yu 		case EVFSNABS:
5256a800f36SLiu Yu 			vc.wp[0] = va.wp[0] | SIGN_BIT_S;
5266a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
5276a800f36SLiu Yu 			goto update_regs;
5286a800f36SLiu Yu 
5296a800f36SLiu Yu 		case EVFSNEG:
5306a800f36SLiu Yu 			vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
5316a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
5326a800f36SLiu Yu 			goto update_regs;
5336a800f36SLiu Yu 
5346a800f36SLiu Yu 		case EVFSADD:
5356a800f36SLiu Yu 			FP_ADD_S(SR0, SA0, SB0);
5366a800f36SLiu Yu 			FP_ADD_S(SR1, SA1, SB1);
5376a800f36SLiu Yu 			goto pack_vs;
5386a800f36SLiu Yu 
5396a800f36SLiu Yu 		case EVFSSUB:
5406a800f36SLiu Yu 			FP_SUB_S(SR0, SA0, SB0);
5416a800f36SLiu Yu 			FP_SUB_S(SR1, SA1, SB1);
5426a800f36SLiu Yu 			goto pack_vs;
5436a800f36SLiu Yu 
5446a800f36SLiu Yu 		case EVFSMUL:
5456a800f36SLiu Yu 			FP_MUL_S(SR0, SA0, SB0);
5466a800f36SLiu Yu 			FP_MUL_S(SR1, SA1, SB1);
5476a800f36SLiu Yu 			goto pack_vs;
5486a800f36SLiu Yu 
5496a800f36SLiu Yu 		case EVFSDIV:
5506a800f36SLiu Yu 			FP_DIV_S(SR0, SA0, SB0);
5516a800f36SLiu Yu 			FP_DIV_S(SR1, SA1, SB1);
5526a800f36SLiu Yu 			goto pack_vs;
5536a800f36SLiu Yu 
5546a800f36SLiu Yu 		case EVFSCMPEQ:
5556a800f36SLiu Yu 			cmp = 0;
5566a800f36SLiu Yu 			goto cmp_vs;
5576a800f36SLiu Yu 
5586a800f36SLiu Yu 		case EVFSCMPGT:
5596a800f36SLiu Yu 			cmp = 1;
5606a800f36SLiu Yu 			goto cmp_vs;
5616a800f36SLiu Yu 
5626a800f36SLiu Yu 		case EVFSCMPLT:
5636a800f36SLiu Yu 			cmp = -1;
5646a800f36SLiu Yu 			goto cmp_vs;
5656a800f36SLiu Yu 
5666a800f36SLiu Yu 		case EVFSCTUF:
56728fbf1d5SJoseph Myers 		case EVFSCTSF:
56828fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
56928fbf1d5SJoseph Myers 				vc.wp[0] = 0;
57028fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
57128fbf1d5SJoseph Myers 			} else {
57228fbf1d5SJoseph Myers 				SB0_e += (func == EVFSCTSF ? 31 : 32);
57328fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32,
5747245fc5bSChristophe Leroy 						(func == EVFSCTSF) ? 1 : 0);
57528fbf1d5SJoseph Myers 			}
57628fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
57728fbf1d5SJoseph Myers 				vc.wp[1] = 0;
57828fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
57928fbf1d5SJoseph Myers 			} else {
58028fbf1d5SJoseph Myers 				SB1_e += (func == EVFSCTSF ? 31 : 32);
58128fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32,
5827245fc5bSChristophe Leroy 						(func == EVFSCTSF) ? 1 : 0);
58328fbf1d5SJoseph Myers 			}
5846a800f36SLiu Yu 			goto update_regs;
5856a800f36SLiu Yu 
5866a800f36SLiu Yu 		case EVFSCTUI:
5876a800f36SLiu Yu 		case EVFSCTSI:
58828fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
58928fbf1d5SJoseph Myers 				vc.wp[0] = 0;
59028fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
59128fbf1d5SJoseph Myers 			} else {
59228fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[0], SB0, 32,
5937245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
59428fbf1d5SJoseph Myers 			}
59528fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
59628fbf1d5SJoseph Myers 				vc.wp[1] = 0;
59728fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
59828fbf1d5SJoseph Myers 			} else {
59928fbf1d5SJoseph Myers 				FP_TO_INT_ROUND_S(vc.wp[1], SB1, 32,
6007245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
60128fbf1d5SJoseph Myers 			}
60228fbf1d5SJoseph Myers 			goto update_regs;
60328fbf1d5SJoseph Myers 
6046a800f36SLiu Yu 		case EVFSCTUIZ:
6056a800f36SLiu Yu 		case EVFSCTSIZ:
60628fbf1d5SJoseph Myers 			if (SB0_c == FP_CLS_NAN) {
60728fbf1d5SJoseph Myers 				vc.wp[0] = 0;
60828fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
6096a800f36SLiu Yu 			} else {
610afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[0], SB0, 32,
6117245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
61228fbf1d5SJoseph Myers 			}
61328fbf1d5SJoseph Myers 			if (SB1_c == FP_CLS_NAN) {
61428fbf1d5SJoseph Myers 				vc.wp[1] = 0;
61528fbf1d5SJoseph Myers 				FP_SET_EXCEPTION(FP_EX_INVALID);
61628fbf1d5SJoseph Myers 			} else {
617afc0a07dSShan Hai 				FP_TO_INT_S(vc.wp[1], SB1, 32,
6187245fc5bSChristophe Leroy 						((func & 0x3) != 0) ? 1 : 0);
61928fbf1d5SJoseph Myers 			}
6206a800f36SLiu Yu 			goto update_regs;
6216a800f36SLiu Yu 
6226a800f36SLiu Yu 		default:
6236a800f36SLiu Yu 			goto illegal;
6246a800f36SLiu Yu 		}
6256a800f36SLiu Yu 		break;
6266a800f36SLiu Yu 
6276a800f36SLiu Yu pack_vs:
6287245fc5bSChristophe Leroy 		pr_debug("SR0: %d %08x %d (%d)\n",
629b430abc4SLiu Yu 				SR0_s, SR0_f, SR0_e, SR0_c);
6307245fc5bSChristophe Leroy 		pr_debug("SR1: %d %08x %d (%d)\n",
631b430abc4SLiu Yu 				SR1_s, SR1_f, SR1_e, SR1_c);
632b430abc4SLiu Yu 
6336a800f36SLiu Yu 		FP_PACK_SP(vc.wp, SR0);
6346a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR1);
6356a800f36SLiu Yu 		goto update_regs;
6366a800f36SLiu Yu 
6376a800f36SLiu Yu cmp_vs:
6386a800f36SLiu Yu 		{
6396a800f36SLiu Yu 			int ch, cl;
6406a800f36SLiu Yu 
6416a800f36SLiu Yu 			FP_CMP_S(IR0, SA0, SB0, 3);
6426a800f36SLiu Yu 			FP_CMP_S(IR1, SA1, SB1, 3);
6436a800f36SLiu Yu 			if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
6446a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6456a800f36SLiu Yu 			if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
6466a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6476a800f36SLiu Yu 			ch = (IR0 == cmp) ? 1 : 0;
6486a800f36SLiu Yu 			cl = (IR1 == cmp) ? 1 : 0;
6496a800f36SLiu Yu 			IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
6506a800f36SLiu Yu 				((ch & cl) << 0);
6516a800f36SLiu Yu 			goto update_ccr;
6526a800f36SLiu Yu 		}
6536a800f36SLiu Yu 	}
6546a800f36SLiu Yu 	default:
6556a800f36SLiu Yu 		return -EINVAL;
6566a800f36SLiu Yu 	}
6576a800f36SLiu Yu 
6586a800f36SLiu Yu update_ccr:
6596a800f36SLiu Yu 	regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6606a800f36SLiu Yu 	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6616a800f36SLiu Yu 
6626a800f36SLiu Yu update_regs:
663640e9225SJoseph Myers 	/*
664640e9225SJoseph Myers 	 * If the "invalid" exception sticky bit was set by the
665640e9225SJoseph Myers 	 * processor for non-finite input, but was not set before the
666640e9225SJoseph Myers 	 * instruction being emulated, clear it.  Likewise for the
667640e9225SJoseph Myers 	 * "underflow" bit, which may have been set by the processor
668640e9225SJoseph Myers 	 * for exact underflow, not just inexact underflow when the
669640e9225SJoseph Myers 	 * flag should be set for IEEE 754 semantics.  Other sticky
670640e9225SJoseph Myers 	 * exceptions will only be set by the processor when they are
671640e9225SJoseph Myers 	 * correct according to IEEE 754 semantics, and we must not
672640e9225SJoseph Myers 	 * clear sticky bits that were already set before the emulated
673640e9225SJoseph Myers 	 * instruction as they represent the user-visible sticky
674640e9225SJoseph Myers 	 * exception status.  "inexact" traps to kernel are not
675640e9225SJoseph Myers 	 * required for IEEE semantics and are not enabled by default,
676640e9225SJoseph Myers 	 * so the "inexact" sticky bit may have been set by a previous
677640e9225SJoseph Myers 	 * instruction without the kernel being aware of it.
678640e9225SJoseph Myers 	 */
679640e9225SJoseph Myers 	__FPU_FPSCR
680640e9225SJoseph Myers 	  &= ~(FP_EX_INVALID | FP_EX_UNDERFLOW) | current->thread.spefscr_last;
6816a800f36SLiu Yu 	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
6826a800f36SLiu Yu 	mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
683640e9225SJoseph Myers 	current->thread.spefscr_last = __FPU_FPSCR;
6846a800f36SLiu Yu 
6856a800f36SLiu Yu 	current->thread.evr[fc] = vc.wp[0];
6866a800f36SLiu Yu 	regs->gpr[fc] = vc.wp[1];
6876a800f36SLiu Yu 
688b430abc4SLiu Yu 	pr_debug("ccr = %08lx\n", regs->ccr);
689b430abc4SLiu Yu 	pr_debug("cur exceptions = %08x spefscr = %08lx\n",
6906a800f36SLiu Yu 			FP_CUR_EXCEPTIONS, __FPU_FPSCR);
691b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
692b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
693b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
6946a800f36SLiu Yu 
69501c9cceeSJoseph Myers 	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE) {
69601c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_DIVZERO)
69701c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_DIV))
69801c9cceeSJoseph Myers 			return 1;
69901c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_OVERFLOW)
70001c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_OVF))
70101c9cceeSJoseph Myers 			return 1;
70201c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_UNDERFLOW)
70301c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_UND))
70401c9cceeSJoseph Myers 			return 1;
70501c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_INEXACT)
70601c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_RES))
70701c9cceeSJoseph Myers 			return 1;
70801c9cceeSJoseph Myers 		if ((FP_CUR_EXCEPTIONS & FP_EX_INVALID)
70901c9cceeSJoseph Myers 		    && (current->thread.fpexc_mode & PR_FP_EXC_INV))
71001c9cceeSJoseph Myers 			return 1;
71101c9cceeSJoseph Myers 	}
7126a800f36SLiu Yu 	return 0;
7136a800f36SLiu Yu 
7146a800f36SLiu Yu illegal:
715ac6f1203SLiu Yu 	if (have_e500_cpu_a005_erratum) {
716ac6f1203SLiu Yu 		/* according to e500 cpu a005 erratum, reissue efp inst */
71759dc5bfcSNicholas Piggin 		regs_add_return_ip(regs, -4);
718b430abc4SLiu Yu 		pr_debug("re-issue efp inst: %08lx\n", speinsn);
719ac6f1203SLiu Yu 		return 0;
720ac6f1203SLiu Yu 	}
721ac6f1203SLiu Yu 
7226a800f36SLiu Yu 	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
7236a800f36SLiu Yu 	return -ENOSYS;
7246a800f36SLiu Yu }
7256a800f36SLiu Yu 
speround_handler(struct pt_regs * regs)7266a800f36SLiu Yu int speround_handler(struct pt_regs *regs)
7276a800f36SLiu Yu {
7286a800f36SLiu Yu 	union dw_union fgpr;
7296a800f36SLiu Yu 	int s_lo, s_hi;
73028414a6dSJoseph Myers 	int lo_inexact, hi_inexact;
73128fbf1d5SJoseph Myers 	int fp_result;
73228fbf1d5SJoseph Myers 	unsigned long speinsn, type, fb, fc, fptype, func;
7336a800f36SLiu Yu 
7346a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
7356a800f36SLiu Yu 		return -EFAULT;
7366a800f36SLiu Yu 	if ((speinsn >> 26) != 4)
7376a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
7386a800f36SLiu Yu 
73928fbf1d5SJoseph Myers 	func = speinsn & 0x7ff;
74028fbf1d5SJoseph Myers 	type = insn_type(func);
7416a800f36SLiu Yu 	if (type == XCR) return -ENOSYS;
7426a800f36SLiu Yu 
743d5755e6fSLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
744d5755e6fSLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
745d5755e6fSLiu Yu 
74628414a6dSJoseph Myers 	fptype = (speinsn >> 5) & 0x7;
74728414a6dSJoseph Myers 
748d5755e6fSLiu Yu 	/* No need to round if the result is exact */
74928414a6dSJoseph Myers 	lo_inexact = __FPU_FPSCR & (SPEFSCR_FG | SPEFSCR_FX);
75028414a6dSJoseph Myers 	hi_inexact = __FPU_FPSCR & (SPEFSCR_FGH | SPEFSCR_FXH);
75128414a6dSJoseph Myers 	if (!(lo_inexact || (hi_inexact && fptype == VCT)))
752d5755e6fSLiu Yu 		return 0;
753d5755e6fSLiu Yu 
7546a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
7556a800f36SLiu Yu 	s_lo = regs->gpr[fc] & SIGN_BIT_S;
7566a800f36SLiu Yu 	s_hi = current->thread.evr[fc] & SIGN_BIT_S;
7576a800f36SLiu Yu 	fgpr.wp[0] = current->thread.evr[fc];
7586a800f36SLiu Yu 	fgpr.wp[1] = regs->gpr[fc];
7596a800f36SLiu Yu 
76028fbf1d5SJoseph Myers 	fb = (speinsn >> 11) & 0x1f;
76128fbf1d5SJoseph Myers 	switch (func) {
76228fbf1d5SJoseph Myers 	case EFSCTUIZ:
76328fbf1d5SJoseph Myers 	case EFSCTSIZ:
76428fbf1d5SJoseph Myers 	case EVFSCTUIZ:
76528fbf1d5SJoseph Myers 	case EVFSCTSIZ:
76628fbf1d5SJoseph Myers 	case EFDCTUIDZ:
76728fbf1d5SJoseph Myers 	case EFDCTSIDZ:
76828fbf1d5SJoseph Myers 	case EFDCTUIZ:
76928fbf1d5SJoseph Myers 	case EFDCTSIZ:
77028fbf1d5SJoseph Myers 		/*
77128fbf1d5SJoseph Myers 		 * These instructions always round to zero,
77228fbf1d5SJoseph Myers 		 * independent of the rounding mode.
77328fbf1d5SJoseph Myers 		 */
77428fbf1d5SJoseph Myers 		return 0;
77528fbf1d5SJoseph Myers 
77628fbf1d5SJoseph Myers 	case EFSCTUI:
77728fbf1d5SJoseph Myers 	case EFSCTUF:
77828fbf1d5SJoseph Myers 	case EVFSCTUI:
77928fbf1d5SJoseph Myers 	case EVFSCTUF:
78028fbf1d5SJoseph Myers 	case EFDCTUI:
78128fbf1d5SJoseph Myers 	case EFDCTUF:
78228fbf1d5SJoseph Myers 		fp_result = 0;
78328fbf1d5SJoseph Myers 		s_lo = 0;
78428fbf1d5SJoseph Myers 		s_hi = 0;
78528fbf1d5SJoseph Myers 		break;
78628fbf1d5SJoseph Myers 
78728fbf1d5SJoseph Myers 	case EFSCTSI:
78828fbf1d5SJoseph Myers 	case EFSCTSF:
78928fbf1d5SJoseph Myers 		fp_result = 0;
79028fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
79128fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
79228fbf1d5SJoseph Myers 			s_lo = regs->gpr[fb] & SIGN_BIT_S;
79328fbf1d5SJoseph Myers 		break;
79428fbf1d5SJoseph Myers 
79528fbf1d5SJoseph Myers 	case EVFSCTSI:
79628fbf1d5SJoseph Myers 	case EVFSCTSF:
79728fbf1d5SJoseph Myers 		fp_result = 0;
79828fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
79928fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
80028fbf1d5SJoseph Myers 			s_lo = regs->gpr[fb] & SIGN_BIT_S;
80128fbf1d5SJoseph Myers 		if (fgpr.wp[0] == 0)
80228fbf1d5SJoseph Myers 			s_hi = current->thread.evr[fb] & SIGN_BIT_S;
80328fbf1d5SJoseph Myers 		break;
80428fbf1d5SJoseph Myers 
80528fbf1d5SJoseph Myers 	case EFDCTSI:
80628fbf1d5SJoseph Myers 	case EFDCTSF:
80728fbf1d5SJoseph Myers 		fp_result = 0;
80828fbf1d5SJoseph Myers 		s_hi = s_lo;
80928fbf1d5SJoseph Myers 		/* Recover the sign of a zero result if possible.  */
81028fbf1d5SJoseph Myers 		if (fgpr.wp[1] == 0)
81128fbf1d5SJoseph Myers 			s_hi = current->thread.evr[fb] & SIGN_BIT_S;
81228fbf1d5SJoseph Myers 		break;
81328fbf1d5SJoseph Myers 
81428fbf1d5SJoseph Myers 	default:
81528fbf1d5SJoseph Myers 		fp_result = 1;
81628fbf1d5SJoseph Myers 		break;
81728fbf1d5SJoseph Myers 	}
81828fbf1d5SJoseph Myers 
819d5755e6fSLiu Yu 	pr_debug("round fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
8206a800f36SLiu Yu 
82128414a6dSJoseph Myers 	switch (fptype) {
8226a800f36SLiu Yu 	/* Since SPE instructions on E500 core can handle round to nearest
8236a800f36SLiu Yu 	 * and round toward zero with IEEE-754 complied, we just need
8246a800f36SLiu Yu 	 * to handle round toward +Inf and round toward -Inf by software.
8256a800f36SLiu Yu 	 */
8266a800f36SLiu Yu 	case SPFP:
8276a800f36SLiu Yu 		if ((FP_ROUNDMODE) == FP_RND_PINF) {
8286a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
8296a800f36SLiu Yu 		} else { /* round to -Inf */
83028fbf1d5SJoseph Myers 			if (s_lo) {
83128fbf1d5SJoseph Myers 				if (fp_result)
83228fbf1d5SJoseph Myers 					fgpr.wp[1]++; /* Z < 0, choose Z2 */
83328fbf1d5SJoseph Myers 				else
83428fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z < 0, choose Z2 */
83528fbf1d5SJoseph Myers 			}
8366a800f36SLiu Yu 		}
8376a800f36SLiu Yu 		break;
8386a800f36SLiu Yu 
8396a800f36SLiu Yu 	case DPFP:
8406a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
84128fbf1d5SJoseph Myers 			if (!s_hi) {
84228fbf1d5SJoseph Myers 				if (fp_result)
84328fbf1d5SJoseph Myers 					fgpr.dp[0]++; /* Z > 0, choose Z1 */
84428fbf1d5SJoseph Myers 				else
84528fbf1d5SJoseph Myers 					fgpr.wp[1]++; /* Z > 0, choose Z1 */
84628fbf1d5SJoseph Myers 			}
8476a800f36SLiu Yu 		} else { /* round to -Inf */
84828fbf1d5SJoseph Myers 			if (s_hi) {
84928fbf1d5SJoseph Myers 				if (fp_result)
85028fbf1d5SJoseph Myers 					fgpr.dp[0]++; /* Z < 0, choose Z2 */
85128fbf1d5SJoseph Myers 				else
85228fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z < 0, choose Z2 */
85328fbf1d5SJoseph Myers 			}
8546a800f36SLiu Yu 		}
8556a800f36SLiu Yu 		break;
8566a800f36SLiu Yu 
8576a800f36SLiu Yu 	case VCT:
8586a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
85928414a6dSJoseph Myers 			if (lo_inexact && !s_lo)
86028414a6dSJoseph Myers 				fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
86128414a6dSJoseph Myers 			if (hi_inexact && !s_hi)
86228414a6dSJoseph Myers 				fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
8636a800f36SLiu Yu 		} else { /* round to -Inf */
86428fbf1d5SJoseph Myers 			if (lo_inexact && s_lo) {
86528fbf1d5SJoseph Myers 				if (fp_result)
86628414a6dSJoseph Myers 					fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
86728fbf1d5SJoseph Myers 				else
86828fbf1d5SJoseph Myers 					fgpr.wp[1]--; /* Z_low < 0, choose Z2 */
86928fbf1d5SJoseph Myers 			}
87028fbf1d5SJoseph Myers 			if (hi_inexact && s_hi) {
87128fbf1d5SJoseph Myers 				if (fp_result)
87228414a6dSJoseph Myers 					fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
87328fbf1d5SJoseph Myers 				else
87428fbf1d5SJoseph Myers 					fgpr.wp[0]--; /* Z_high < 0, choose Z2 */
87528fbf1d5SJoseph Myers 			}
8766a800f36SLiu Yu 		}
8776a800f36SLiu Yu 		break;
8786a800f36SLiu Yu 
8796a800f36SLiu Yu 	default:
8806a800f36SLiu Yu 		return -EINVAL;
8816a800f36SLiu Yu 	}
8826a800f36SLiu Yu 
8836a800f36SLiu Yu 	current->thread.evr[fc] = fgpr.wp[0];
8846a800f36SLiu Yu 	regs->gpr[fc] = fgpr.wp[1];
8856a800f36SLiu Yu 
886d5755e6fSLiu Yu 	pr_debug("  to fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
887d5755e6fSLiu Yu 
88801c9cceeSJoseph Myers 	if (current->thread.fpexc_mode & PR_FP_EXC_SW_ENABLE)
88901c9cceeSJoseph Myers 		return (current->thread.fpexc_mode & PR_FP_EXC_RES) ? 1 : 0;
8906a800f36SLiu Yu 	return 0;
8916a800f36SLiu Yu }
892ac6f1203SLiu Yu 
spe_mathemu_init(void)893*78c73c80SChristophe Leroy static int __init spe_mathemu_init(void)
894ac6f1203SLiu Yu {
895ac6f1203SLiu Yu 	u32 pvr, maj, min;
896ac6f1203SLiu Yu 
897ac6f1203SLiu Yu 	pvr = mfspr(SPRN_PVR);
898ac6f1203SLiu Yu 
899ac6f1203SLiu Yu 	if ((PVR_VER(pvr) == PVR_VER_E500V1) ||
900ac6f1203SLiu Yu 	    (PVR_VER(pvr) == PVR_VER_E500V2)) {
901ac6f1203SLiu Yu 		maj = PVR_MAJ(pvr);
902ac6f1203SLiu Yu 		min = PVR_MIN(pvr);
903ac6f1203SLiu Yu 
904ac6f1203SLiu Yu 		/*
905ac6f1203SLiu Yu 		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1
906ac6f1203SLiu Yu 		 * need cpu a005 errata workaround
907ac6f1203SLiu Yu 		 */
908ac6f1203SLiu Yu 		switch (maj) {
909ac6f1203SLiu Yu 		case 1:
910ac6f1203SLiu Yu 			if (min < 1)
911ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
912ac6f1203SLiu Yu 			break;
913ac6f1203SLiu Yu 		case 2:
914ac6f1203SLiu Yu 			if (min < 3)
915ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
916ac6f1203SLiu Yu 			break;
917ac6f1203SLiu Yu 		case 3:
918ac6f1203SLiu Yu 		case 4:
919ac6f1203SLiu Yu 		case 5:
920ac6f1203SLiu Yu 			if (min < 1)
921ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
922ac6f1203SLiu Yu 			break;
923ac6f1203SLiu Yu 		default:
924ac6f1203SLiu Yu 			break;
925ac6f1203SLiu Yu 		}
926ac6f1203SLiu Yu 	}
927ac6f1203SLiu Yu 
928ac6f1203SLiu Yu 	return 0;
929ac6f1203SLiu Yu }
930ac6f1203SLiu Yu 
931ac6f1203SLiu Yu module_init(spe_mathemu_init);
932