xref: /openbmc/linux/arch/powerpc/math-emu/math_efp.c (revision 28414a6d)
16a800f36SLiu Yu /*
26a800f36SLiu Yu  * arch/powerpc/math-emu/math_efp.c
36a800f36SLiu Yu  *
4ac6f1203SLiu Yu  * Copyright (C) 2006-2008, 2010 Freescale Semiconductor, Inc.
56a800f36SLiu Yu  *
66a800f36SLiu Yu  * Author: Ebony Zhu,	<ebony.zhu@freescale.com>
76a800f36SLiu Yu  *         Yu Liu,	<yu.liu@freescale.com>
86a800f36SLiu Yu  *
96a800f36SLiu Yu  * Derived from arch/alpha/math-emu/math.c
106a800f36SLiu Yu  *              arch/powerpc/math-emu/math.c
116a800f36SLiu Yu  *
126a800f36SLiu Yu  * Description:
136a800f36SLiu Yu  * This file is the exception handler to make E500 SPE instructions
146a800f36SLiu Yu  * fully comply with IEEE-754 floating point standard.
156a800f36SLiu Yu  *
166a800f36SLiu Yu  * This program is free software; you can redistribute it and/or
176a800f36SLiu Yu  * modify it under the terms of the GNU General Public License
186a800f36SLiu Yu  * as published by the Free Software Foundation; either version
196a800f36SLiu Yu  * 2 of the License, or (at your option) any later version.
206a800f36SLiu Yu  */
216a800f36SLiu Yu 
226a800f36SLiu Yu #include <linux/types.h>
236a800f36SLiu Yu 
246a800f36SLiu Yu #include <asm/uaccess.h>
256a800f36SLiu Yu #include <asm/reg.h>
266a800f36SLiu Yu 
276a800f36SLiu Yu #define FP_EX_BOOKE_E500_SPE
286a800f36SLiu Yu #include <asm/sfp-machine.h>
296a800f36SLiu Yu 
306a800f36SLiu Yu #include <math-emu/soft-fp.h>
316a800f36SLiu Yu #include <math-emu/single.h>
326a800f36SLiu Yu #include <math-emu/double.h>
336a800f36SLiu Yu 
346a800f36SLiu Yu #define EFAPU		0x4
356a800f36SLiu Yu 
366a800f36SLiu Yu #define VCT		0x4
376a800f36SLiu Yu #define SPFP		0x6
386a800f36SLiu Yu #define DPFP		0x7
396a800f36SLiu Yu 
406a800f36SLiu Yu #define EFSADD		0x2c0
416a800f36SLiu Yu #define EFSSUB		0x2c1
426a800f36SLiu Yu #define EFSABS		0x2c4
436a800f36SLiu Yu #define EFSNABS		0x2c5
446a800f36SLiu Yu #define EFSNEG		0x2c6
456a800f36SLiu Yu #define EFSMUL		0x2c8
466a800f36SLiu Yu #define EFSDIV		0x2c9
476a800f36SLiu Yu #define EFSCMPGT	0x2cc
486a800f36SLiu Yu #define EFSCMPLT	0x2cd
496a800f36SLiu Yu #define EFSCMPEQ	0x2ce
506a800f36SLiu Yu #define EFSCFD		0x2cf
516a800f36SLiu Yu #define EFSCFSI		0x2d1
526a800f36SLiu Yu #define EFSCTUI		0x2d4
536a800f36SLiu Yu #define EFSCTSI		0x2d5
546a800f36SLiu Yu #define EFSCTUF		0x2d6
556a800f36SLiu Yu #define EFSCTSF		0x2d7
566a800f36SLiu Yu #define EFSCTUIZ	0x2d8
576a800f36SLiu Yu #define EFSCTSIZ	0x2da
586a800f36SLiu Yu 
596a800f36SLiu Yu #define EVFSADD		0x280
606a800f36SLiu Yu #define EVFSSUB		0x281
616a800f36SLiu Yu #define EVFSABS		0x284
626a800f36SLiu Yu #define EVFSNABS	0x285
636a800f36SLiu Yu #define EVFSNEG		0x286
646a800f36SLiu Yu #define EVFSMUL		0x288
656a800f36SLiu Yu #define EVFSDIV		0x289
666a800f36SLiu Yu #define EVFSCMPGT	0x28c
676a800f36SLiu Yu #define EVFSCMPLT	0x28d
686a800f36SLiu Yu #define EVFSCMPEQ	0x28e
696a800f36SLiu Yu #define EVFSCTUI	0x294
706a800f36SLiu Yu #define EVFSCTSI	0x295
716a800f36SLiu Yu #define EVFSCTUF	0x296
726a800f36SLiu Yu #define EVFSCTSF	0x297
736a800f36SLiu Yu #define EVFSCTUIZ	0x298
746a800f36SLiu Yu #define EVFSCTSIZ	0x29a
756a800f36SLiu Yu 
766a800f36SLiu Yu #define EFDADD		0x2e0
776a800f36SLiu Yu #define EFDSUB		0x2e1
786a800f36SLiu Yu #define EFDABS		0x2e4
796a800f36SLiu Yu #define EFDNABS		0x2e5
806a800f36SLiu Yu #define EFDNEG		0x2e6
816a800f36SLiu Yu #define EFDMUL		0x2e8
826a800f36SLiu Yu #define EFDDIV		0x2e9
836a800f36SLiu Yu #define EFDCTUIDZ	0x2ea
846a800f36SLiu Yu #define EFDCTSIDZ	0x2eb
856a800f36SLiu Yu #define EFDCMPGT	0x2ec
866a800f36SLiu Yu #define EFDCMPLT	0x2ed
876a800f36SLiu Yu #define EFDCMPEQ	0x2ee
886a800f36SLiu Yu #define EFDCFS		0x2ef
896a800f36SLiu Yu #define EFDCTUI		0x2f4
906a800f36SLiu Yu #define EFDCTSI		0x2f5
916a800f36SLiu Yu #define EFDCTUF		0x2f6
926a800f36SLiu Yu #define EFDCTSF		0x2f7
936a800f36SLiu Yu #define EFDCTUIZ	0x2f8
946a800f36SLiu Yu #define EFDCTSIZ	0x2fa
956a800f36SLiu Yu 
966a800f36SLiu Yu #define AB	2
976a800f36SLiu Yu #define XA	3
986a800f36SLiu Yu #define XB	4
996a800f36SLiu Yu #define XCR	5
1006a800f36SLiu Yu #define NOTYPE	0
1016a800f36SLiu Yu 
1026a800f36SLiu Yu #define SIGN_BIT_S	(1UL << 31)
1036a800f36SLiu Yu #define SIGN_BIT_D	(1ULL << 63)
1046a800f36SLiu Yu #define FP_EX_MASK	(FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \
1056a800f36SLiu Yu 			FP_EX_UNDERFLOW | FP_EX_OVERFLOW)
1066a800f36SLiu Yu 
107ac6f1203SLiu Yu static int have_e500_cpu_a005_erratum;
108ac6f1203SLiu Yu 
1096a800f36SLiu Yu union dw_union {
1106a800f36SLiu Yu 	u64 dp[1];
1116a800f36SLiu Yu 	u32 wp[2];
1126a800f36SLiu Yu };
1136a800f36SLiu Yu 
1146a800f36SLiu Yu static unsigned long insn_type(unsigned long speinsn)
1156a800f36SLiu Yu {
1166a800f36SLiu Yu 	unsigned long ret = NOTYPE;
1176a800f36SLiu Yu 
1186a800f36SLiu Yu 	switch (speinsn & 0x7ff) {
1196a800f36SLiu Yu 	case EFSABS:	ret = XA;	break;
1206a800f36SLiu Yu 	case EFSADD:	ret = AB;	break;
1216a800f36SLiu Yu 	case EFSCFD:	ret = XB;	break;
1226a800f36SLiu Yu 	case EFSCMPEQ:	ret = XCR;	break;
1236a800f36SLiu Yu 	case EFSCMPGT:	ret = XCR;	break;
1246a800f36SLiu Yu 	case EFSCMPLT:	ret = XCR;	break;
1256a800f36SLiu Yu 	case EFSCTSF:	ret = XB;	break;
1266a800f36SLiu Yu 	case EFSCTSI:	ret = XB;	break;
1276a800f36SLiu Yu 	case EFSCTSIZ:	ret = XB;	break;
1286a800f36SLiu Yu 	case EFSCTUF:	ret = XB;	break;
1296a800f36SLiu Yu 	case EFSCTUI:	ret = XB;	break;
1306a800f36SLiu Yu 	case EFSCTUIZ:	ret = XB;	break;
1316a800f36SLiu Yu 	case EFSDIV:	ret = AB;	break;
1326a800f36SLiu Yu 	case EFSMUL:	ret = AB;	break;
1336a800f36SLiu Yu 	case EFSNABS:	ret = XA;	break;
1346a800f36SLiu Yu 	case EFSNEG:	ret = XA;	break;
1356a800f36SLiu Yu 	case EFSSUB:	ret = AB;	break;
1366a800f36SLiu Yu 	case EFSCFSI:	ret = XB;	break;
1376a800f36SLiu Yu 
1386a800f36SLiu Yu 	case EVFSABS:	ret = XA;	break;
1396a800f36SLiu Yu 	case EVFSADD:	ret = AB;	break;
1406a800f36SLiu Yu 	case EVFSCMPEQ:	ret = XCR;	break;
1416a800f36SLiu Yu 	case EVFSCMPGT:	ret = XCR;	break;
1426a800f36SLiu Yu 	case EVFSCMPLT:	ret = XCR;	break;
1436a800f36SLiu Yu 	case EVFSCTSF:	ret = XB;	break;
1446a800f36SLiu Yu 	case EVFSCTSI:	ret = XB;	break;
1456a800f36SLiu Yu 	case EVFSCTSIZ:	ret = XB;	break;
1466a800f36SLiu Yu 	case EVFSCTUF:	ret = XB;	break;
1476a800f36SLiu Yu 	case EVFSCTUI:	ret = XB;	break;
1486a800f36SLiu Yu 	case EVFSCTUIZ:	ret = XB;	break;
1496a800f36SLiu Yu 	case EVFSDIV:	ret = AB;	break;
1506a800f36SLiu Yu 	case EVFSMUL:	ret = AB;	break;
1516a800f36SLiu Yu 	case EVFSNABS:	ret = XA;	break;
1526a800f36SLiu Yu 	case EVFSNEG:	ret = XA;	break;
1536a800f36SLiu Yu 	case EVFSSUB:	ret = AB;	break;
1546a800f36SLiu Yu 
1556a800f36SLiu Yu 	case EFDABS:	ret = XA;	break;
1566a800f36SLiu Yu 	case EFDADD:	ret = AB;	break;
1576a800f36SLiu Yu 	case EFDCFS:	ret = XB;	break;
1586a800f36SLiu Yu 	case EFDCMPEQ:	ret = XCR;	break;
1596a800f36SLiu Yu 	case EFDCMPGT:	ret = XCR;	break;
1606a800f36SLiu Yu 	case EFDCMPLT:	ret = XCR;	break;
1616a800f36SLiu Yu 	case EFDCTSF:	ret = XB;	break;
1626a800f36SLiu Yu 	case EFDCTSI:	ret = XB;	break;
1636a800f36SLiu Yu 	case EFDCTSIDZ:	ret = XB;	break;
1646a800f36SLiu Yu 	case EFDCTSIZ:	ret = XB;	break;
1656a800f36SLiu Yu 	case EFDCTUF:	ret = XB;	break;
1666a800f36SLiu Yu 	case EFDCTUI:	ret = XB;	break;
1676a800f36SLiu Yu 	case EFDCTUIDZ:	ret = XB;	break;
1686a800f36SLiu Yu 	case EFDCTUIZ:	ret = XB;	break;
1696a800f36SLiu Yu 	case EFDDIV:	ret = AB;	break;
1706a800f36SLiu Yu 	case EFDMUL:	ret = AB;	break;
1716a800f36SLiu Yu 	case EFDNABS:	ret = XA;	break;
1726a800f36SLiu Yu 	case EFDNEG:	ret = XA;	break;
1736a800f36SLiu Yu 	case EFDSUB:	ret = AB;	break;
1746a800f36SLiu Yu 	}
1756a800f36SLiu Yu 
1766a800f36SLiu Yu 	return ret;
1776a800f36SLiu Yu }
1786a800f36SLiu Yu 
1796a800f36SLiu Yu int do_spe_mathemu(struct pt_regs *regs)
1806a800f36SLiu Yu {
1816a800f36SLiu Yu 	FP_DECL_EX;
1826a800f36SLiu Yu 	int IR, cmp;
1836a800f36SLiu Yu 
1846a800f36SLiu Yu 	unsigned long type, func, fc, fa, fb, src, speinsn;
1856a800f36SLiu Yu 	union dw_union vc, va, vb;
1866a800f36SLiu Yu 
1876a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
1886a800f36SLiu Yu 		return -EFAULT;
1896a800f36SLiu Yu 	if ((speinsn >> 26) != EFAPU)
1906a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
1916a800f36SLiu Yu 
1926a800f36SLiu Yu 	type = insn_type(speinsn);
1936a800f36SLiu Yu 	if (type == NOTYPE)
19409af52f7SLiu Yu 		goto illegal;
1956a800f36SLiu Yu 
1966a800f36SLiu Yu 	func = speinsn & 0x7ff;
1976a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
1986a800f36SLiu Yu 	fa = (speinsn >> 16) & 0x1f;
1996a800f36SLiu Yu 	fb = (speinsn >> 11) & 0x1f;
2006a800f36SLiu Yu 	src = (speinsn >> 5) & 0x7;
2016a800f36SLiu Yu 
2026a800f36SLiu Yu 	vc.wp[0] = current->thread.evr[fc];
2036a800f36SLiu Yu 	vc.wp[1] = regs->gpr[fc];
2046a800f36SLiu Yu 	va.wp[0] = current->thread.evr[fa];
2056a800f36SLiu Yu 	va.wp[1] = regs->gpr[fa];
2066a800f36SLiu Yu 	vb.wp[0] = current->thread.evr[fb];
2076a800f36SLiu Yu 	vb.wp[1] = regs->gpr[fb];
2086a800f36SLiu Yu 
2096a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
2106a800f36SLiu Yu 
211b430abc4SLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
212b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
213b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
214b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
2156a800f36SLiu Yu 
2166a800f36SLiu Yu 	switch (src) {
2176a800f36SLiu Yu 	case SPFP: {
2186a800f36SLiu Yu 		FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
2196a800f36SLiu Yu 
2206a800f36SLiu Yu 		switch (type) {
2216a800f36SLiu Yu 		case AB:
2226a800f36SLiu Yu 		case XCR:
2236a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2246a800f36SLiu Yu 		case XB:
2256a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
2266a800f36SLiu Yu 			break;
2276a800f36SLiu Yu 		case XA:
2286a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2296a800f36SLiu Yu 			break;
2306a800f36SLiu Yu 		}
2316a800f36SLiu Yu 
232b430abc4SLiu Yu 		pr_debug("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c);
233b430abc4SLiu Yu 		pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);
2346a800f36SLiu Yu 
2356a800f36SLiu Yu 		switch (func) {
2366a800f36SLiu Yu 		case EFSABS:
2376a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
2386a800f36SLiu Yu 			goto update_regs;
2396a800f36SLiu Yu 
2406a800f36SLiu Yu 		case EFSNABS:
2416a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
2426a800f36SLiu Yu 			goto update_regs;
2436a800f36SLiu Yu 
2446a800f36SLiu Yu 		case EFSNEG:
2456a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
2466a800f36SLiu Yu 			goto update_regs;
2476a800f36SLiu Yu 
2486a800f36SLiu Yu 		case EFSADD:
2496a800f36SLiu Yu 			FP_ADD_S(SR, SA, SB);
2506a800f36SLiu Yu 			goto pack_s;
2516a800f36SLiu Yu 
2526a800f36SLiu Yu 		case EFSSUB:
2536a800f36SLiu Yu 			FP_SUB_S(SR, SA, SB);
2546a800f36SLiu Yu 			goto pack_s;
2556a800f36SLiu Yu 
2566a800f36SLiu Yu 		case EFSMUL:
2576a800f36SLiu Yu 			FP_MUL_S(SR, SA, SB);
2586a800f36SLiu Yu 			goto pack_s;
2596a800f36SLiu Yu 
2606a800f36SLiu Yu 		case EFSDIV:
2616a800f36SLiu Yu 			FP_DIV_S(SR, SA, SB);
2626a800f36SLiu Yu 			goto pack_s;
2636a800f36SLiu Yu 
2646a800f36SLiu Yu 		case EFSCMPEQ:
2656a800f36SLiu Yu 			cmp = 0;
2666a800f36SLiu Yu 			goto cmp_s;
2676a800f36SLiu Yu 
2686a800f36SLiu Yu 		case EFSCMPGT:
2696a800f36SLiu Yu 			cmp = 1;
2706a800f36SLiu Yu 			goto cmp_s;
2716a800f36SLiu Yu 
2726a800f36SLiu Yu 		case EFSCMPLT:
2736a800f36SLiu Yu 			cmp = -1;
2746a800f36SLiu Yu 			goto cmp_s;
2756a800f36SLiu Yu 
2766a800f36SLiu Yu 		case EFSCTSF:
2776a800f36SLiu Yu 		case EFSCTUF:
2786a800f36SLiu Yu 			if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) {
2796a800f36SLiu Yu 				/* NaN */
2806a800f36SLiu Yu 				if (((vb.wp[1] >> 23) & 0xff) == 0) {
2816a800f36SLiu Yu 					/* denorm */
2826a800f36SLiu Yu 					vc.wp[1] = 0x0;
2836a800f36SLiu Yu 				} else if ((vb.wp[1] >> 31) == 0) {
2846a800f36SLiu Yu 					/* positive normal */
2856a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2866a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
2876a800f36SLiu Yu 				} else { /* negative normal */
2886a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2896a800f36SLiu Yu 						0x80000000 : 0x0;
2906a800f36SLiu Yu 				}
2916a800f36SLiu Yu 			} else { /* rB is NaN */
2926a800f36SLiu Yu 				vc.wp[1] = 0x0;
2936a800f36SLiu Yu 			}
2946a800f36SLiu Yu 			goto update_regs;
2956a800f36SLiu Yu 
2966a800f36SLiu Yu 		case EFSCFD: {
2976a800f36SLiu Yu 			FP_DECL_D(DB);
2986a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
2996a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
300b430abc4SLiu Yu 
301b430abc4SLiu Yu 			pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
3026a800f36SLiu Yu 					DB_s, DB_f1, DB_f0, DB_e, DB_c);
303b430abc4SLiu Yu 
3046a800f36SLiu Yu 			FP_CONV(S, D, 1, 2, SR, DB);
3056a800f36SLiu Yu 			goto pack_s;
3066a800f36SLiu Yu 		}
3076a800f36SLiu Yu 
3086a800f36SLiu Yu 		case EFSCTSI:
3096a800f36SLiu Yu 		case EFSCTSIZ:
3106a800f36SLiu Yu 		case EFSCTUI:
3116a800f36SLiu Yu 		case EFSCTUIZ:
3126a800f36SLiu Yu 			if (func & 0x4) {
3136a800f36SLiu Yu 				_FP_ROUND(1, SB);
3146a800f36SLiu Yu 			} else {
3156a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB);
3166a800f36SLiu Yu 			}
317afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB, 32,
318afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB_s));
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:
327b430abc4SLiu Yu 		pr_debug("SR: %ld %08lx %ld (%ld)\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);
3516a800f36SLiu Yu 		case XB:
3526a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3536a800f36SLiu Yu 			break;
3546a800f36SLiu Yu 		case XA:
3556a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3566a800f36SLiu Yu 			break;
3576a800f36SLiu Yu 		}
3586a800f36SLiu Yu 
359b430abc4SLiu Yu 		pr_debug("DA: %ld %08lx %08lx %ld (%ld)\n",
3606a800f36SLiu Yu 				DA_s, DA_f1, DA_f0, DA_e, DA_c);
361b430abc4SLiu Yu 		pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
3626a800f36SLiu Yu 				DB_s, DB_f1, DB_f0, DB_e, DB_c);
3636a800f36SLiu Yu 
3646a800f36SLiu Yu 		switch (func) {
3656a800f36SLiu Yu 		case EFDABS:
3666a800f36SLiu Yu 			vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
3676a800f36SLiu Yu 			goto update_regs;
3686a800f36SLiu Yu 
3696a800f36SLiu Yu 		case EFDNABS:
3706a800f36SLiu Yu 			vc.dp[0] = va.dp[0] | SIGN_BIT_D;
3716a800f36SLiu Yu 			goto update_regs;
3726a800f36SLiu Yu 
3736a800f36SLiu Yu 		case EFDNEG:
3746a800f36SLiu Yu 			vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
3756a800f36SLiu Yu 			goto update_regs;
3766a800f36SLiu Yu 
3776a800f36SLiu Yu 		case EFDADD:
3786a800f36SLiu Yu 			FP_ADD_D(DR, DA, DB);
3796a800f36SLiu Yu 			goto pack_d;
3806a800f36SLiu Yu 
3816a800f36SLiu Yu 		case EFDSUB:
3826a800f36SLiu Yu 			FP_SUB_D(DR, DA, DB);
3836a800f36SLiu Yu 			goto pack_d;
3846a800f36SLiu Yu 
3856a800f36SLiu Yu 		case EFDMUL:
3866a800f36SLiu Yu 			FP_MUL_D(DR, DA, DB);
3876a800f36SLiu Yu 			goto pack_d;
3886a800f36SLiu Yu 
3896a800f36SLiu Yu 		case EFDDIV:
3906a800f36SLiu Yu 			FP_DIV_D(DR, DA, DB);
3916a800f36SLiu Yu 			goto pack_d;
3926a800f36SLiu Yu 
3936a800f36SLiu Yu 		case EFDCMPEQ:
3946a800f36SLiu Yu 			cmp = 0;
3956a800f36SLiu Yu 			goto cmp_d;
3966a800f36SLiu Yu 
3976a800f36SLiu Yu 		case EFDCMPGT:
3986a800f36SLiu Yu 			cmp = 1;
3996a800f36SLiu Yu 			goto cmp_d;
4006a800f36SLiu Yu 
4016a800f36SLiu Yu 		case EFDCMPLT:
4026a800f36SLiu Yu 			cmp = -1;
4036a800f36SLiu Yu 			goto cmp_d;
4046a800f36SLiu Yu 
4056a800f36SLiu Yu 		case EFDCTSF:
4066a800f36SLiu Yu 		case EFDCTUF:
4076a800f36SLiu Yu 			if (!((vb.wp[0] >> 20) == 0x7ff &&
4086a800f36SLiu Yu 			   ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) {
4096a800f36SLiu Yu 				/* not a NaN */
4106a800f36SLiu Yu 				if (((vb.wp[0] >> 20) & 0x7ff) == 0) {
4116a800f36SLiu Yu 					/* denorm */
4126a800f36SLiu Yu 					vc.wp[1] = 0x0;
4136a800f36SLiu Yu 				} else if ((vb.wp[0] >> 31) == 0) {
4146a800f36SLiu Yu 					/* positive normal */
4156a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4166a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
4176a800f36SLiu Yu 				} else { /* negative normal */
4186a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4196a800f36SLiu Yu 						0x80000000 : 0x0;
4206a800f36SLiu Yu 				}
4216a800f36SLiu Yu 			} else { /* NaN */
4226a800f36SLiu Yu 				vc.wp[1] = 0x0;
4236a800f36SLiu Yu 			}
4246a800f36SLiu Yu 			goto update_regs;
4256a800f36SLiu Yu 
4266a800f36SLiu Yu 		case EFDCFS: {
4276a800f36SLiu Yu 			FP_DECL_S(SB);
4286a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
4296a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
430b430abc4SLiu Yu 
431b430abc4SLiu Yu 			pr_debug("SB: %ld %08lx %ld (%ld)\n",
4326a800f36SLiu Yu 					SB_s, SB_f, SB_e, SB_c);
433b430abc4SLiu Yu 
4346a800f36SLiu Yu 			FP_CONV(D, S, 2, 1, DR, SB);
4356a800f36SLiu Yu 			goto pack_d;
4366a800f36SLiu Yu 		}
4376a800f36SLiu Yu 
4386a800f36SLiu Yu 		case EFDCTUIDZ:
4396a800f36SLiu Yu 		case EFDCTSIDZ:
4406a800f36SLiu Yu 			_FP_ROUND_ZERO(2, DB);
4416a800f36SLiu Yu 			FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0));
4426a800f36SLiu Yu 			goto update_regs;
4436a800f36SLiu Yu 
4446a800f36SLiu Yu 		case EFDCTUI:
4456a800f36SLiu Yu 		case EFDCTSI:
4466a800f36SLiu Yu 		case EFDCTUIZ:
4476a800f36SLiu Yu 		case EFDCTSIZ:
4486a800f36SLiu Yu 			if (func & 0x4) {
4496a800f36SLiu Yu 				_FP_ROUND(2, DB);
4506a800f36SLiu Yu 			} else {
4516a800f36SLiu Yu 				_FP_ROUND_ZERO(2, DB);
4526a800f36SLiu Yu 			}
453afc0a07dSShan Hai 			FP_TO_INT_D(vc.wp[1], DB, 32,
454afc0a07dSShan Hai 					(((func & 0x3) != 0) || DB_s));
4556a800f36SLiu Yu 			goto update_regs;
4566a800f36SLiu Yu 
4576a800f36SLiu Yu 		default:
4586a800f36SLiu Yu 			goto illegal;
4596a800f36SLiu Yu 		}
4606a800f36SLiu Yu 		break;
4616a800f36SLiu Yu 
4626a800f36SLiu Yu pack_d:
463b430abc4SLiu Yu 		pr_debug("DR: %ld %08lx %08lx %ld (%ld)\n",
4646a800f36SLiu Yu 				DR_s, DR_f1, DR_f0, DR_e, DR_c);
465b430abc4SLiu Yu 
4666a800f36SLiu Yu 		FP_PACK_DP(vc.dp, DR);
4676a800f36SLiu Yu 		goto update_regs;
4686a800f36SLiu Yu 
4696a800f36SLiu Yu cmp_d:
4706a800f36SLiu Yu 		FP_CMP_D(IR, DA, DB, 3);
4716a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
4726a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
4736a800f36SLiu Yu 		if (IR == cmp) {
4746a800f36SLiu Yu 			IR = 0x4;
4756a800f36SLiu Yu 		} else {
4766a800f36SLiu Yu 			IR = 0;
4776a800f36SLiu Yu 		}
4786a800f36SLiu Yu 		goto update_ccr;
4796a800f36SLiu Yu 
4806a800f36SLiu Yu 	}
4816a800f36SLiu Yu 
4826a800f36SLiu Yu 	case VCT: {
4836a800f36SLiu Yu 		FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
4846a800f36SLiu Yu 		FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
4856a800f36SLiu Yu 		int IR0, IR1;
4866a800f36SLiu Yu 
4876a800f36SLiu Yu 		switch (type) {
4886a800f36SLiu Yu 		case AB:
4896a800f36SLiu Yu 		case XCR:
4906a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
4916a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
4926a800f36SLiu Yu 		case XB:
4936a800f36SLiu Yu 			FP_UNPACK_SP(SB0, vb.wp);
4946a800f36SLiu Yu 			FP_UNPACK_SP(SB1, vb.wp + 1);
4956a800f36SLiu Yu 			break;
4966a800f36SLiu Yu 		case XA:
4976a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
4986a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
4996a800f36SLiu Yu 			break;
5006a800f36SLiu Yu 		}
5016a800f36SLiu Yu 
502b430abc4SLiu Yu 		pr_debug("SA0: %ld %08lx %ld (%ld)\n",
503b430abc4SLiu Yu 				SA0_s, SA0_f, SA0_e, SA0_c);
504b430abc4SLiu Yu 		pr_debug("SA1: %ld %08lx %ld (%ld)\n",
505b430abc4SLiu Yu 				SA1_s, SA1_f, SA1_e, SA1_c);
506b430abc4SLiu Yu 		pr_debug("SB0: %ld %08lx %ld (%ld)\n",
507b430abc4SLiu Yu 				SB0_s, SB0_f, SB0_e, SB0_c);
508b430abc4SLiu Yu 		pr_debug("SB1: %ld %08lx %ld (%ld)\n",
509b430abc4SLiu Yu 				SB1_s, SB1_f, SB1_e, SB1_c);
5106a800f36SLiu Yu 
5116a800f36SLiu Yu 		switch (func) {
5126a800f36SLiu Yu 		case EVFSABS:
5136a800f36SLiu Yu 			vc.wp[0] = va.wp[0] & ~SIGN_BIT_S;
5146a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
5156a800f36SLiu Yu 			goto update_regs;
5166a800f36SLiu Yu 
5176a800f36SLiu Yu 		case EVFSNABS:
5186a800f36SLiu Yu 			vc.wp[0] = va.wp[0] | SIGN_BIT_S;
5196a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
5206a800f36SLiu Yu 			goto update_regs;
5216a800f36SLiu Yu 
5226a800f36SLiu Yu 		case EVFSNEG:
5236a800f36SLiu Yu 			vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
5246a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
5256a800f36SLiu Yu 			goto update_regs;
5266a800f36SLiu Yu 
5276a800f36SLiu Yu 		case EVFSADD:
5286a800f36SLiu Yu 			FP_ADD_S(SR0, SA0, SB0);
5296a800f36SLiu Yu 			FP_ADD_S(SR1, SA1, SB1);
5306a800f36SLiu Yu 			goto pack_vs;
5316a800f36SLiu Yu 
5326a800f36SLiu Yu 		case EVFSSUB:
5336a800f36SLiu Yu 			FP_SUB_S(SR0, SA0, SB0);
5346a800f36SLiu Yu 			FP_SUB_S(SR1, SA1, SB1);
5356a800f36SLiu Yu 			goto pack_vs;
5366a800f36SLiu Yu 
5376a800f36SLiu Yu 		case EVFSMUL:
5386a800f36SLiu Yu 			FP_MUL_S(SR0, SA0, SB0);
5396a800f36SLiu Yu 			FP_MUL_S(SR1, SA1, SB1);
5406a800f36SLiu Yu 			goto pack_vs;
5416a800f36SLiu Yu 
5426a800f36SLiu Yu 		case EVFSDIV:
5436a800f36SLiu Yu 			FP_DIV_S(SR0, SA0, SB0);
5446a800f36SLiu Yu 			FP_DIV_S(SR1, SA1, SB1);
5456a800f36SLiu Yu 			goto pack_vs;
5466a800f36SLiu Yu 
5476a800f36SLiu Yu 		case EVFSCMPEQ:
5486a800f36SLiu Yu 			cmp = 0;
5496a800f36SLiu Yu 			goto cmp_vs;
5506a800f36SLiu Yu 
5516a800f36SLiu Yu 		case EVFSCMPGT:
5526a800f36SLiu Yu 			cmp = 1;
5536a800f36SLiu Yu 			goto cmp_vs;
5546a800f36SLiu Yu 
5556a800f36SLiu Yu 		case EVFSCMPLT:
5566a800f36SLiu Yu 			cmp = -1;
5576a800f36SLiu Yu 			goto cmp_vs;
5586a800f36SLiu Yu 
5596a800f36SLiu Yu 		case EVFSCTSF:
5606a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5616a800f36SLiu Yu 				"efsctsf %0, %2\n"
5626a800f36SLiu Yu 				"efsctsf %1, %3\n"
5636a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5646a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5656a800f36SLiu Yu 			goto update_regs;
5666a800f36SLiu Yu 
5676a800f36SLiu Yu 		case EVFSCTUF:
5686a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5696a800f36SLiu Yu 				"efsctuf %0, %2\n"
5706a800f36SLiu Yu 				"efsctuf %1, %3\n"
5716a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5726a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5736a800f36SLiu Yu 			goto update_regs;
5746a800f36SLiu Yu 
5756a800f36SLiu Yu 		case EVFSCTUI:
5766a800f36SLiu Yu 		case EVFSCTSI:
5776a800f36SLiu Yu 		case EVFSCTUIZ:
5786a800f36SLiu Yu 		case EVFSCTSIZ:
5796a800f36SLiu Yu 			if (func & 0x4) {
5806a800f36SLiu Yu 				_FP_ROUND(1, SB0);
5816a800f36SLiu Yu 				_FP_ROUND(1, SB1);
5826a800f36SLiu Yu 			} else {
5836a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB0);
5846a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB1);
5856a800f36SLiu Yu 			}
586afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[0], SB0, 32,
587afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB0_s));
588afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB1, 32,
589afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB1_s));
5906a800f36SLiu Yu 			goto update_regs;
5916a800f36SLiu Yu 
5926a800f36SLiu Yu 		default:
5936a800f36SLiu Yu 			goto illegal;
5946a800f36SLiu Yu 		}
5956a800f36SLiu Yu 		break;
5966a800f36SLiu Yu 
5976a800f36SLiu Yu pack_vs:
598b430abc4SLiu Yu 		pr_debug("SR0: %ld %08lx %ld (%ld)\n",
599b430abc4SLiu Yu 				SR0_s, SR0_f, SR0_e, SR0_c);
600b430abc4SLiu Yu 		pr_debug("SR1: %ld %08lx %ld (%ld)\n",
601b430abc4SLiu Yu 				SR1_s, SR1_f, SR1_e, SR1_c);
602b430abc4SLiu Yu 
6036a800f36SLiu Yu 		FP_PACK_SP(vc.wp, SR0);
6046a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR1);
6056a800f36SLiu Yu 		goto update_regs;
6066a800f36SLiu Yu 
6076a800f36SLiu Yu cmp_vs:
6086a800f36SLiu Yu 		{
6096a800f36SLiu Yu 			int ch, cl;
6106a800f36SLiu Yu 
6116a800f36SLiu Yu 			FP_CMP_S(IR0, SA0, SB0, 3);
6126a800f36SLiu Yu 			FP_CMP_S(IR1, SA1, SB1, 3);
6136a800f36SLiu Yu 			if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
6146a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6156a800f36SLiu Yu 			if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
6166a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6176a800f36SLiu Yu 			ch = (IR0 == cmp) ? 1 : 0;
6186a800f36SLiu Yu 			cl = (IR1 == cmp) ? 1 : 0;
6196a800f36SLiu Yu 			IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
6206a800f36SLiu Yu 				((ch & cl) << 0);
6216a800f36SLiu Yu 			goto update_ccr;
6226a800f36SLiu Yu 		}
6236a800f36SLiu Yu 	}
6246a800f36SLiu Yu 	default:
6256a800f36SLiu Yu 		return -EINVAL;
6266a800f36SLiu Yu 	}
6276a800f36SLiu Yu 
6286a800f36SLiu Yu update_ccr:
6296a800f36SLiu Yu 	regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6306a800f36SLiu Yu 	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6316a800f36SLiu Yu 
6326a800f36SLiu Yu update_regs:
633640e9225SJoseph Myers 	/*
634640e9225SJoseph Myers 	 * If the "invalid" exception sticky bit was set by the
635640e9225SJoseph Myers 	 * processor for non-finite input, but was not set before the
636640e9225SJoseph Myers 	 * instruction being emulated, clear it.  Likewise for the
637640e9225SJoseph Myers 	 * "underflow" bit, which may have been set by the processor
638640e9225SJoseph Myers 	 * for exact underflow, not just inexact underflow when the
639640e9225SJoseph Myers 	 * flag should be set for IEEE 754 semantics.  Other sticky
640640e9225SJoseph Myers 	 * exceptions will only be set by the processor when they are
641640e9225SJoseph Myers 	 * correct according to IEEE 754 semantics, and we must not
642640e9225SJoseph Myers 	 * clear sticky bits that were already set before the emulated
643640e9225SJoseph Myers 	 * instruction as they represent the user-visible sticky
644640e9225SJoseph Myers 	 * exception status.  "inexact" traps to kernel are not
645640e9225SJoseph Myers 	 * required for IEEE semantics and are not enabled by default,
646640e9225SJoseph Myers 	 * so the "inexact" sticky bit may have been set by a previous
647640e9225SJoseph Myers 	 * instruction without the kernel being aware of it.
648640e9225SJoseph Myers 	 */
649640e9225SJoseph Myers 	__FPU_FPSCR
650640e9225SJoseph Myers 	  &= ~(FP_EX_INVALID | FP_EX_UNDERFLOW) | current->thread.spefscr_last;
6516a800f36SLiu Yu 	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
6526a800f36SLiu Yu 	mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
653640e9225SJoseph Myers 	current->thread.spefscr_last = __FPU_FPSCR;
6546a800f36SLiu Yu 
6556a800f36SLiu Yu 	current->thread.evr[fc] = vc.wp[0];
6566a800f36SLiu Yu 	regs->gpr[fc] = vc.wp[1];
6576a800f36SLiu Yu 
658b430abc4SLiu Yu 	pr_debug("ccr = %08lx\n", regs->ccr);
659b430abc4SLiu Yu 	pr_debug("cur exceptions = %08x spefscr = %08lx\n",
6606a800f36SLiu Yu 			FP_CUR_EXCEPTIONS, __FPU_FPSCR);
661b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
662b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
663b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
6646a800f36SLiu Yu 
6656a800f36SLiu Yu 	return 0;
6666a800f36SLiu Yu 
6676a800f36SLiu Yu illegal:
668ac6f1203SLiu Yu 	if (have_e500_cpu_a005_erratum) {
669ac6f1203SLiu Yu 		/* according to e500 cpu a005 erratum, reissue efp inst */
670ac6f1203SLiu Yu 		regs->nip -= 4;
671b430abc4SLiu Yu 		pr_debug("re-issue efp inst: %08lx\n", speinsn);
672ac6f1203SLiu Yu 		return 0;
673ac6f1203SLiu Yu 	}
674ac6f1203SLiu Yu 
6756a800f36SLiu Yu 	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
6766a800f36SLiu Yu 	return -ENOSYS;
6776a800f36SLiu Yu }
6786a800f36SLiu Yu 
6796a800f36SLiu Yu int speround_handler(struct pt_regs *regs)
6806a800f36SLiu Yu {
6816a800f36SLiu Yu 	union dw_union fgpr;
6826a800f36SLiu Yu 	int s_lo, s_hi;
68328414a6dSJoseph Myers 	int lo_inexact, hi_inexact;
68428414a6dSJoseph Myers 	unsigned long speinsn, type, fc, fptype;
6856a800f36SLiu Yu 
6866a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
6876a800f36SLiu Yu 		return -EFAULT;
6886a800f36SLiu Yu 	if ((speinsn >> 26) != 4)
6896a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
6906a800f36SLiu Yu 
6916a800f36SLiu Yu 	type = insn_type(speinsn & 0x7ff);
6926a800f36SLiu Yu 	if (type == XCR) return -ENOSYS;
6936a800f36SLiu Yu 
694d5755e6fSLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
695d5755e6fSLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
696d5755e6fSLiu Yu 
69728414a6dSJoseph Myers 	fptype = (speinsn >> 5) & 0x7;
69828414a6dSJoseph Myers 
699d5755e6fSLiu Yu 	/* No need to round if the result is exact */
70028414a6dSJoseph Myers 	lo_inexact = __FPU_FPSCR & (SPEFSCR_FG | SPEFSCR_FX);
70128414a6dSJoseph Myers 	hi_inexact = __FPU_FPSCR & (SPEFSCR_FGH | SPEFSCR_FXH);
70228414a6dSJoseph Myers 	if (!(lo_inexact || (hi_inexact && fptype == VCT)))
703d5755e6fSLiu Yu 		return 0;
704d5755e6fSLiu Yu 
7056a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
7066a800f36SLiu Yu 	s_lo = regs->gpr[fc] & SIGN_BIT_S;
7076a800f36SLiu Yu 	s_hi = current->thread.evr[fc] & SIGN_BIT_S;
7086a800f36SLiu Yu 	fgpr.wp[0] = current->thread.evr[fc];
7096a800f36SLiu Yu 	fgpr.wp[1] = regs->gpr[fc];
7106a800f36SLiu Yu 
711d5755e6fSLiu Yu 	pr_debug("round fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
7126a800f36SLiu Yu 
71328414a6dSJoseph Myers 	switch (fptype) {
7146a800f36SLiu Yu 	/* Since SPE instructions on E500 core can handle round to nearest
7156a800f36SLiu Yu 	 * and round toward zero with IEEE-754 complied, we just need
7166a800f36SLiu Yu 	 * to handle round toward +Inf and round toward -Inf by software.
7176a800f36SLiu Yu 	 */
7186a800f36SLiu Yu 	case SPFP:
7196a800f36SLiu Yu 		if ((FP_ROUNDMODE) == FP_RND_PINF) {
7206a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
7216a800f36SLiu Yu 		} else { /* round to -Inf */
7226a800f36SLiu Yu 			if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */
7236a800f36SLiu Yu 		}
7246a800f36SLiu Yu 		break;
7256a800f36SLiu Yu 
7266a800f36SLiu Yu 	case DPFP:
7276a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
7286a800f36SLiu Yu 			if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */
7296a800f36SLiu Yu 		} else { /* round to -Inf */
7306a800f36SLiu Yu 			if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */
7316a800f36SLiu Yu 		}
7326a800f36SLiu Yu 		break;
7336a800f36SLiu Yu 
7346a800f36SLiu Yu 	case VCT:
7356a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
73628414a6dSJoseph Myers 			if (lo_inexact && !s_lo)
73728414a6dSJoseph Myers 				fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
73828414a6dSJoseph Myers 			if (hi_inexact && !s_hi)
73928414a6dSJoseph Myers 				fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
7406a800f36SLiu Yu 		} else { /* round to -Inf */
74128414a6dSJoseph Myers 			if (lo_inexact && s_lo)
74228414a6dSJoseph Myers 				fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
74328414a6dSJoseph Myers 			if (hi_inexact && s_hi)
74428414a6dSJoseph Myers 				fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
7456a800f36SLiu Yu 		}
7466a800f36SLiu Yu 		break;
7476a800f36SLiu Yu 
7486a800f36SLiu Yu 	default:
7496a800f36SLiu Yu 		return -EINVAL;
7506a800f36SLiu Yu 	}
7516a800f36SLiu Yu 
7526a800f36SLiu Yu 	current->thread.evr[fc] = fgpr.wp[0];
7536a800f36SLiu Yu 	regs->gpr[fc] = fgpr.wp[1];
7546a800f36SLiu Yu 
755d5755e6fSLiu Yu 	pr_debug("  to fgpr: %08x  %08x\n", fgpr.wp[0], fgpr.wp[1]);
756d5755e6fSLiu Yu 
7576a800f36SLiu Yu 	return 0;
7586a800f36SLiu Yu }
759ac6f1203SLiu Yu 
760ac6f1203SLiu Yu int __init spe_mathemu_init(void)
761ac6f1203SLiu Yu {
762ac6f1203SLiu Yu 	u32 pvr, maj, min;
763ac6f1203SLiu Yu 
764ac6f1203SLiu Yu 	pvr = mfspr(SPRN_PVR);
765ac6f1203SLiu Yu 
766ac6f1203SLiu Yu 	if ((PVR_VER(pvr) == PVR_VER_E500V1) ||
767ac6f1203SLiu Yu 	    (PVR_VER(pvr) == PVR_VER_E500V2)) {
768ac6f1203SLiu Yu 		maj = PVR_MAJ(pvr);
769ac6f1203SLiu Yu 		min = PVR_MIN(pvr);
770ac6f1203SLiu Yu 
771ac6f1203SLiu Yu 		/*
772ac6f1203SLiu Yu 		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1
773ac6f1203SLiu Yu 		 * need cpu a005 errata workaround
774ac6f1203SLiu Yu 		 */
775ac6f1203SLiu Yu 		switch (maj) {
776ac6f1203SLiu Yu 		case 1:
777ac6f1203SLiu Yu 			if (min < 1)
778ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
779ac6f1203SLiu Yu 			break;
780ac6f1203SLiu Yu 		case 2:
781ac6f1203SLiu Yu 			if (min < 3)
782ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
783ac6f1203SLiu Yu 			break;
784ac6f1203SLiu Yu 		case 3:
785ac6f1203SLiu Yu 		case 4:
786ac6f1203SLiu Yu 		case 5:
787ac6f1203SLiu Yu 			if (min < 1)
788ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
789ac6f1203SLiu Yu 			break;
790ac6f1203SLiu Yu 		default:
791ac6f1203SLiu Yu 			break;
792ac6f1203SLiu Yu 		}
793ac6f1203SLiu Yu 	}
794ac6f1203SLiu Yu 
795ac6f1203SLiu Yu 	return 0;
796ac6f1203SLiu Yu }
797ac6f1203SLiu Yu 
798ac6f1203SLiu Yu module_init(spe_mathemu_init);
799