xref: /openbmc/linux/arch/powerpc/math-emu/math_efp.c (revision afc0a07d)
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 	default:
1766a800f36SLiu Yu 		printk(KERN_ERR "\nOoops! SPE instruction no type found.");
1776a800f36SLiu Yu 		printk(KERN_ERR "\ninst code: %08lx\n", speinsn);
1786a800f36SLiu Yu 	}
1796a800f36SLiu Yu 
1806a800f36SLiu Yu 	return ret;
1816a800f36SLiu Yu }
1826a800f36SLiu Yu 
1836a800f36SLiu Yu int do_spe_mathemu(struct pt_regs *regs)
1846a800f36SLiu Yu {
1856a800f36SLiu Yu 	FP_DECL_EX;
1866a800f36SLiu Yu 	int IR, cmp;
1876a800f36SLiu Yu 
1886a800f36SLiu Yu 	unsigned long type, func, fc, fa, fb, src, speinsn;
1896a800f36SLiu Yu 	union dw_union vc, va, vb;
1906a800f36SLiu Yu 
1916a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
1926a800f36SLiu Yu 		return -EFAULT;
1936a800f36SLiu Yu 	if ((speinsn >> 26) != EFAPU)
1946a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
1956a800f36SLiu Yu 
1966a800f36SLiu Yu 	type = insn_type(speinsn);
1976a800f36SLiu Yu 	if (type == NOTYPE)
1986a800f36SLiu Yu 		return -ENOSYS;
1996a800f36SLiu Yu 
2006a800f36SLiu Yu 	func = speinsn & 0x7ff;
2016a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
2026a800f36SLiu Yu 	fa = (speinsn >> 16) & 0x1f;
2036a800f36SLiu Yu 	fb = (speinsn >> 11) & 0x1f;
2046a800f36SLiu Yu 	src = (speinsn >> 5) & 0x7;
2056a800f36SLiu Yu 
2066a800f36SLiu Yu 	vc.wp[0] = current->thread.evr[fc];
2076a800f36SLiu Yu 	vc.wp[1] = regs->gpr[fc];
2086a800f36SLiu Yu 	va.wp[0] = current->thread.evr[fa];
2096a800f36SLiu Yu 	va.wp[1] = regs->gpr[fa];
2106a800f36SLiu Yu 	vb.wp[0] = current->thread.evr[fb];
2116a800f36SLiu Yu 	vb.wp[1] = regs->gpr[fb];
2126a800f36SLiu Yu 
2136a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
2146a800f36SLiu Yu 
2156a800f36SLiu Yu #ifdef DEBUG
2166a800f36SLiu Yu 	printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
2176a800f36SLiu Yu 	printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
2186a800f36SLiu Yu 	printk("va: %08x  %08x\n", va.wp[0], va.wp[1]);
2196a800f36SLiu Yu 	printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
2206a800f36SLiu Yu #endif
2216a800f36SLiu Yu 
2226a800f36SLiu Yu 	switch (src) {
2236a800f36SLiu Yu 	case SPFP: {
2246a800f36SLiu Yu 		FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
2256a800f36SLiu Yu 
2266a800f36SLiu Yu 		switch (type) {
2276a800f36SLiu Yu 		case AB:
2286a800f36SLiu Yu 		case XCR:
2296a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2306a800f36SLiu Yu 		case XB:
2316a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
2326a800f36SLiu Yu 			break;
2336a800f36SLiu Yu 		case XA:
2346a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2356a800f36SLiu Yu 			break;
2366a800f36SLiu Yu 		}
2376a800f36SLiu Yu 
2386a800f36SLiu Yu #ifdef DEBUG
2396a800f36SLiu Yu 		printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c);
2406a800f36SLiu Yu 		printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);
2416a800f36SLiu Yu #endif
2426a800f36SLiu Yu 
2436a800f36SLiu Yu 		switch (func) {
2446a800f36SLiu Yu 		case EFSABS:
2456a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
2466a800f36SLiu Yu 			goto update_regs;
2476a800f36SLiu Yu 
2486a800f36SLiu Yu 		case EFSNABS:
2496a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
2506a800f36SLiu Yu 			goto update_regs;
2516a800f36SLiu Yu 
2526a800f36SLiu Yu 		case EFSNEG:
2536a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
2546a800f36SLiu Yu 			goto update_regs;
2556a800f36SLiu Yu 
2566a800f36SLiu Yu 		case EFSADD:
2576a800f36SLiu Yu 			FP_ADD_S(SR, SA, SB);
2586a800f36SLiu Yu 			goto pack_s;
2596a800f36SLiu Yu 
2606a800f36SLiu Yu 		case EFSSUB:
2616a800f36SLiu Yu 			FP_SUB_S(SR, SA, SB);
2626a800f36SLiu Yu 			goto pack_s;
2636a800f36SLiu Yu 
2646a800f36SLiu Yu 		case EFSMUL:
2656a800f36SLiu Yu 			FP_MUL_S(SR, SA, SB);
2666a800f36SLiu Yu 			goto pack_s;
2676a800f36SLiu Yu 
2686a800f36SLiu Yu 		case EFSDIV:
2696a800f36SLiu Yu 			FP_DIV_S(SR, SA, SB);
2706a800f36SLiu Yu 			goto pack_s;
2716a800f36SLiu Yu 
2726a800f36SLiu Yu 		case EFSCMPEQ:
2736a800f36SLiu Yu 			cmp = 0;
2746a800f36SLiu Yu 			goto cmp_s;
2756a800f36SLiu Yu 
2766a800f36SLiu Yu 		case EFSCMPGT:
2776a800f36SLiu Yu 			cmp = 1;
2786a800f36SLiu Yu 			goto cmp_s;
2796a800f36SLiu Yu 
2806a800f36SLiu Yu 		case EFSCMPLT:
2816a800f36SLiu Yu 			cmp = -1;
2826a800f36SLiu Yu 			goto cmp_s;
2836a800f36SLiu Yu 
2846a800f36SLiu Yu 		case EFSCTSF:
2856a800f36SLiu Yu 		case EFSCTUF:
2866a800f36SLiu Yu 			if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) {
2876a800f36SLiu Yu 				/* NaN */
2886a800f36SLiu Yu 				if (((vb.wp[1] >> 23) & 0xff) == 0) {
2896a800f36SLiu Yu 					/* denorm */
2906a800f36SLiu Yu 					vc.wp[1] = 0x0;
2916a800f36SLiu Yu 				} else if ((vb.wp[1] >> 31) == 0) {
2926a800f36SLiu Yu 					/* positive normal */
2936a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2946a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
2956a800f36SLiu Yu 				} else { /* negative normal */
2966a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2976a800f36SLiu Yu 						0x80000000 : 0x0;
2986a800f36SLiu Yu 				}
2996a800f36SLiu Yu 			} else { /* rB is NaN */
3006a800f36SLiu Yu 				vc.wp[1] = 0x0;
3016a800f36SLiu Yu 			}
3026a800f36SLiu Yu 			goto update_regs;
3036a800f36SLiu Yu 
3046a800f36SLiu Yu 		case EFSCFD: {
3056a800f36SLiu Yu 			FP_DECL_D(DB);
3066a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
3076a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3086a800f36SLiu Yu #ifdef DEBUG
3096a800f36SLiu Yu 			printk("DB: %ld %08lx %08lx %ld (%ld)\n",
3106a800f36SLiu Yu 					DB_s, DB_f1, DB_f0, DB_e, DB_c);
3116a800f36SLiu Yu #endif
3126a800f36SLiu Yu 			FP_CONV(S, D, 1, 2, SR, DB);
3136a800f36SLiu Yu 			goto pack_s;
3146a800f36SLiu Yu 		}
3156a800f36SLiu Yu 
3166a800f36SLiu Yu 		case EFSCTSI:
3176a800f36SLiu Yu 		case EFSCTSIZ:
3186a800f36SLiu Yu 		case EFSCTUI:
3196a800f36SLiu Yu 		case EFSCTUIZ:
3206a800f36SLiu Yu 			if (func & 0x4) {
3216a800f36SLiu Yu 				_FP_ROUND(1, SB);
3226a800f36SLiu Yu 			} else {
3236a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB);
3246a800f36SLiu Yu 			}
325afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB, 32,
326afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB_s));
3276a800f36SLiu Yu 			goto update_regs;
3286a800f36SLiu Yu 
3296a800f36SLiu Yu 		default:
3306a800f36SLiu Yu 			goto illegal;
3316a800f36SLiu Yu 		}
3326a800f36SLiu Yu 		break;
3336a800f36SLiu Yu 
3346a800f36SLiu Yu pack_s:
3356a800f36SLiu Yu #ifdef DEBUG
3366a800f36SLiu Yu 		printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c);
3376a800f36SLiu Yu #endif
3386a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR);
3396a800f36SLiu Yu 		goto update_regs;
3406a800f36SLiu Yu 
3416a800f36SLiu Yu cmp_s:
3426a800f36SLiu Yu 		FP_CMP_S(IR, SA, SB, 3);
3436a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB)))
3446a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
3456a800f36SLiu Yu 		if (IR == cmp) {
3466a800f36SLiu Yu 			IR = 0x4;
3476a800f36SLiu Yu 		} else {
3486a800f36SLiu Yu 			IR = 0;
3496a800f36SLiu Yu 		}
3506a800f36SLiu Yu 		goto update_ccr;
3516a800f36SLiu Yu 	}
3526a800f36SLiu Yu 
3536a800f36SLiu Yu 	case DPFP: {
3546a800f36SLiu Yu 		FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
3556a800f36SLiu Yu 
3566a800f36SLiu Yu 		switch (type) {
3576a800f36SLiu Yu 		case AB:
3586a800f36SLiu Yu 		case XCR:
3596a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3606a800f36SLiu Yu 		case XB:
3616a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3626a800f36SLiu Yu 			break;
3636a800f36SLiu Yu 		case XA:
3646a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3656a800f36SLiu Yu 			break;
3666a800f36SLiu Yu 		}
3676a800f36SLiu Yu 
3686a800f36SLiu Yu #ifdef DEBUG
3696a800f36SLiu Yu 		printk("DA: %ld %08lx %08lx %ld (%ld)\n",
3706a800f36SLiu Yu 				DA_s, DA_f1, DA_f0, DA_e, DA_c);
3716a800f36SLiu Yu 		printk("DB: %ld %08lx %08lx %ld (%ld)\n",
3726a800f36SLiu Yu 				DB_s, DB_f1, DB_f0, DB_e, DB_c);
3736a800f36SLiu Yu #endif
3746a800f36SLiu Yu 
3756a800f36SLiu Yu 		switch (func) {
3766a800f36SLiu Yu 		case EFDABS:
3776a800f36SLiu Yu 			vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
3786a800f36SLiu Yu 			goto update_regs;
3796a800f36SLiu Yu 
3806a800f36SLiu Yu 		case EFDNABS:
3816a800f36SLiu Yu 			vc.dp[0] = va.dp[0] | SIGN_BIT_D;
3826a800f36SLiu Yu 			goto update_regs;
3836a800f36SLiu Yu 
3846a800f36SLiu Yu 		case EFDNEG:
3856a800f36SLiu Yu 			vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
3866a800f36SLiu Yu 			goto update_regs;
3876a800f36SLiu Yu 
3886a800f36SLiu Yu 		case EFDADD:
3896a800f36SLiu Yu 			FP_ADD_D(DR, DA, DB);
3906a800f36SLiu Yu 			goto pack_d;
3916a800f36SLiu Yu 
3926a800f36SLiu Yu 		case EFDSUB:
3936a800f36SLiu Yu 			FP_SUB_D(DR, DA, DB);
3946a800f36SLiu Yu 			goto pack_d;
3956a800f36SLiu Yu 
3966a800f36SLiu Yu 		case EFDMUL:
3976a800f36SLiu Yu 			FP_MUL_D(DR, DA, DB);
3986a800f36SLiu Yu 			goto pack_d;
3996a800f36SLiu Yu 
4006a800f36SLiu Yu 		case EFDDIV:
4016a800f36SLiu Yu 			FP_DIV_D(DR, DA, DB);
4026a800f36SLiu Yu 			goto pack_d;
4036a800f36SLiu Yu 
4046a800f36SLiu Yu 		case EFDCMPEQ:
4056a800f36SLiu Yu 			cmp = 0;
4066a800f36SLiu Yu 			goto cmp_d;
4076a800f36SLiu Yu 
4086a800f36SLiu Yu 		case EFDCMPGT:
4096a800f36SLiu Yu 			cmp = 1;
4106a800f36SLiu Yu 			goto cmp_d;
4116a800f36SLiu Yu 
4126a800f36SLiu Yu 		case EFDCMPLT:
4136a800f36SLiu Yu 			cmp = -1;
4146a800f36SLiu Yu 			goto cmp_d;
4156a800f36SLiu Yu 
4166a800f36SLiu Yu 		case EFDCTSF:
4176a800f36SLiu Yu 		case EFDCTUF:
4186a800f36SLiu Yu 			if (!((vb.wp[0] >> 20) == 0x7ff &&
4196a800f36SLiu Yu 			   ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) {
4206a800f36SLiu Yu 				/* not a NaN */
4216a800f36SLiu Yu 				if (((vb.wp[0] >> 20) & 0x7ff) == 0) {
4226a800f36SLiu Yu 					/* denorm */
4236a800f36SLiu Yu 					vc.wp[1] = 0x0;
4246a800f36SLiu Yu 				} else if ((vb.wp[0] >> 31) == 0) {
4256a800f36SLiu Yu 					/* positive normal */
4266a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4276a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
4286a800f36SLiu Yu 				} else { /* negative normal */
4296a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4306a800f36SLiu Yu 						0x80000000 : 0x0;
4316a800f36SLiu Yu 				}
4326a800f36SLiu Yu 			} else { /* NaN */
4336a800f36SLiu Yu 				vc.wp[1] = 0x0;
4346a800f36SLiu Yu 			}
4356a800f36SLiu Yu 			goto update_regs;
4366a800f36SLiu Yu 
4376a800f36SLiu Yu 		case EFDCFS: {
4386a800f36SLiu Yu 			FP_DECL_S(SB);
4396a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
4406a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
4416a800f36SLiu Yu #ifdef DEBUG
4426a800f36SLiu Yu 			printk("SB: %ld %08lx %ld (%ld)\n",
4436a800f36SLiu Yu 					SB_s, SB_f, SB_e, SB_c);
4446a800f36SLiu Yu #endif
4456a800f36SLiu Yu 			FP_CONV(D, S, 2, 1, DR, SB);
4466a800f36SLiu Yu 			goto pack_d;
4476a800f36SLiu Yu 		}
4486a800f36SLiu Yu 
4496a800f36SLiu Yu 		case EFDCTUIDZ:
4506a800f36SLiu Yu 		case EFDCTSIDZ:
4516a800f36SLiu Yu 			_FP_ROUND_ZERO(2, DB);
4526a800f36SLiu Yu 			FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0));
4536a800f36SLiu Yu 			goto update_regs;
4546a800f36SLiu Yu 
4556a800f36SLiu Yu 		case EFDCTUI:
4566a800f36SLiu Yu 		case EFDCTSI:
4576a800f36SLiu Yu 		case EFDCTUIZ:
4586a800f36SLiu Yu 		case EFDCTSIZ:
4596a800f36SLiu Yu 			if (func & 0x4) {
4606a800f36SLiu Yu 				_FP_ROUND(2, DB);
4616a800f36SLiu Yu 			} else {
4626a800f36SLiu Yu 				_FP_ROUND_ZERO(2, DB);
4636a800f36SLiu Yu 			}
464afc0a07dSShan Hai 			FP_TO_INT_D(vc.wp[1], DB, 32,
465afc0a07dSShan Hai 					(((func & 0x3) != 0) || DB_s));
4666a800f36SLiu Yu 			goto update_regs;
4676a800f36SLiu Yu 
4686a800f36SLiu Yu 		default:
4696a800f36SLiu Yu 			goto illegal;
4706a800f36SLiu Yu 		}
4716a800f36SLiu Yu 		break;
4726a800f36SLiu Yu 
4736a800f36SLiu Yu pack_d:
4746a800f36SLiu Yu #ifdef DEBUG
4756a800f36SLiu Yu 		printk("DR: %ld %08lx %08lx %ld (%ld)\n",
4766a800f36SLiu Yu 				DR_s, DR_f1, DR_f0, DR_e, DR_c);
4776a800f36SLiu Yu #endif
4786a800f36SLiu Yu 		FP_PACK_DP(vc.dp, DR);
4796a800f36SLiu Yu 		goto update_regs;
4806a800f36SLiu Yu 
4816a800f36SLiu Yu cmp_d:
4826a800f36SLiu Yu 		FP_CMP_D(IR, DA, DB, 3);
4836a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
4846a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
4856a800f36SLiu Yu 		if (IR == cmp) {
4866a800f36SLiu Yu 			IR = 0x4;
4876a800f36SLiu Yu 		} else {
4886a800f36SLiu Yu 			IR = 0;
4896a800f36SLiu Yu 		}
4906a800f36SLiu Yu 		goto update_ccr;
4916a800f36SLiu Yu 
4926a800f36SLiu Yu 	}
4936a800f36SLiu Yu 
4946a800f36SLiu Yu 	case VCT: {
4956a800f36SLiu Yu 		FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
4966a800f36SLiu Yu 		FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
4976a800f36SLiu Yu 		int IR0, IR1;
4986a800f36SLiu Yu 
4996a800f36SLiu Yu 		switch (type) {
5006a800f36SLiu Yu 		case AB:
5016a800f36SLiu Yu 		case XCR:
5026a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
5036a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
5046a800f36SLiu Yu 		case XB:
5056a800f36SLiu Yu 			FP_UNPACK_SP(SB0, vb.wp);
5066a800f36SLiu Yu 			FP_UNPACK_SP(SB1, vb.wp + 1);
5076a800f36SLiu Yu 			break;
5086a800f36SLiu Yu 		case XA:
5096a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
5106a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
5116a800f36SLiu Yu 			break;
5126a800f36SLiu Yu 		}
5136a800f36SLiu Yu 
5146a800f36SLiu Yu #ifdef DEBUG
5156a800f36SLiu Yu 		printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c);
5166a800f36SLiu Yu 		printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c);
5176a800f36SLiu Yu 		printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c);
5186a800f36SLiu Yu 		printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c);
5196a800f36SLiu Yu #endif
5206a800f36SLiu Yu 
5216a800f36SLiu Yu 		switch (func) {
5226a800f36SLiu Yu 		case EVFSABS:
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 EVFSNABS:
5286a800f36SLiu Yu 			vc.wp[0] = va.wp[0] | SIGN_BIT_S;
5296a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
5306a800f36SLiu Yu 			goto update_regs;
5316a800f36SLiu Yu 
5326a800f36SLiu Yu 		case EVFSNEG:
5336a800f36SLiu Yu 			vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
5346a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
5356a800f36SLiu Yu 			goto update_regs;
5366a800f36SLiu Yu 
5376a800f36SLiu Yu 		case EVFSADD:
5386a800f36SLiu Yu 			FP_ADD_S(SR0, SA0, SB0);
5396a800f36SLiu Yu 			FP_ADD_S(SR1, SA1, SB1);
5406a800f36SLiu Yu 			goto pack_vs;
5416a800f36SLiu Yu 
5426a800f36SLiu Yu 		case EVFSSUB:
5436a800f36SLiu Yu 			FP_SUB_S(SR0, SA0, SB0);
5446a800f36SLiu Yu 			FP_SUB_S(SR1, SA1, SB1);
5456a800f36SLiu Yu 			goto pack_vs;
5466a800f36SLiu Yu 
5476a800f36SLiu Yu 		case EVFSMUL:
5486a800f36SLiu Yu 			FP_MUL_S(SR0, SA0, SB0);
5496a800f36SLiu Yu 			FP_MUL_S(SR1, SA1, SB1);
5506a800f36SLiu Yu 			goto pack_vs;
5516a800f36SLiu Yu 
5526a800f36SLiu Yu 		case EVFSDIV:
5536a800f36SLiu Yu 			FP_DIV_S(SR0, SA0, SB0);
5546a800f36SLiu Yu 			FP_DIV_S(SR1, SA1, SB1);
5556a800f36SLiu Yu 			goto pack_vs;
5566a800f36SLiu Yu 
5576a800f36SLiu Yu 		case EVFSCMPEQ:
5586a800f36SLiu Yu 			cmp = 0;
5596a800f36SLiu Yu 			goto cmp_vs;
5606a800f36SLiu Yu 
5616a800f36SLiu Yu 		case EVFSCMPGT:
5626a800f36SLiu Yu 			cmp = 1;
5636a800f36SLiu Yu 			goto cmp_vs;
5646a800f36SLiu Yu 
5656a800f36SLiu Yu 		case EVFSCMPLT:
5666a800f36SLiu Yu 			cmp = -1;
5676a800f36SLiu Yu 			goto cmp_vs;
5686a800f36SLiu Yu 
5696a800f36SLiu Yu 		case EVFSCTSF:
5706a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5716a800f36SLiu Yu 				"efsctsf %0, %2\n"
5726a800f36SLiu Yu 				"efsctsf %1, %3\n"
5736a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5746a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5756a800f36SLiu Yu 			goto update_regs;
5766a800f36SLiu Yu 
5776a800f36SLiu Yu 		case EVFSCTUF:
5786a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5796a800f36SLiu Yu 				"efsctuf %0, %2\n"
5806a800f36SLiu Yu 				"efsctuf %1, %3\n"
5816a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5826a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5836a800f36SLiu Yu 			goto update_regs;
5846a800f36SLiu Yu 
5856a800f36SLiu Yu 		case EVFSCTUI:
5866a800f36SLiu Yu 		case EVFSCTSI:
5876a800f36SLiu Yu 		case EVFSCTUIZ:
5886a800f36SLiu Yu 		case EVFSCTSIZ:
5896a800f36SLiu Yu 			if (func & 0x4) {
5906a800f36SLiu Yu 				_FP_ROUND(1, SB0);
5916a800f36SLiu Yu 				_FP_ROUND(1, SB1);
5926a800f36SLiu Yu 			} else {
5936a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB0);
5946a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB1);
5956a800f36SLiu Yu 			}
596afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[0], SB0, 32,
597afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB0_s));
598afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB1, 32,
599afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB1_s));
6006a800f36SLiu Yu 			goto update_regs;
6016a800f36SLiu Yu 
6026a800f36SLiu Yu 		default:
6036a800f36SLiu Yu 			goto illegal;
6046a800f36SLiu Yu 		}
6056a800f36SLiu Yu 		break;
6066a800f36SLiu Yu 
6076a800f36SLiu Yu pack_vs:
6086a800f36SLiu Yu #ifdef DEBUG
6096a800f36SLiu Yu 		printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c);
6106a800f36SLiu Yu 		printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c);
6116a800f36SLiu Yu #endif
6126a800f36SLiu Yu 		FP_PACK_SP(vc.wp, SR0);
6136a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR1);
6146a800f36SLiu Yu 		goto update_regs;
6156a800f36SLiu Yu 
6166a800f36SLiu Yu cmp_vs:
6176a800f36SLiu Yu 		{
6186a800f36SLiu Yu 			int ch, cl;
6196a800f36SLiu Yu 
6206a800f36SLiu Yu 			FP_CMP_S(IR0, SA0, SB0, 3);
6216a800f36SLiu Yu 			FP_CMP_S(IR1, SA1, SB1, 3);
6226a800f36SLiu Yu 			if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
6236a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6246a800f36SLiu Yu 			if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
6256a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6266a800f36SLiu Yu 			ch = (IR0 == cmp) ? 1 : 0;
6276a800f36SLiu Yu 			cl = (IR1 == cmp) ? 1 : 0;
6286a800f36SLiu Yu 			IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
6296a800f36SLiu Yu 				((ch & cl) << 0);
6306a800f36SLiu Yu 			goto update_ccr;
6316a800f36SLiu Yu 		}
6326a800f36SLiu Yu 	}
6336a800f36SLiu Yu 	default:
6346a800f36SLiu Yu 		return -EINVAL;
6356a800f36SLiu Yu 	}
6366a800f36SLiu Yu 
6376a800f36SLiu Yu update_ccr:
6386a800f36SLiu Yu 	regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6396a800f36SLiu Yu 	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6406a800f36SLiu Yu 
6416a800f36SLiu Yu update_regs:
6426a800f36SLiu Yu 	__FPU_FPSCR &= ~FP_EX_MASK;
6436a800f36SLiu Yu 	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
6446a800f36SLiu Yu 	mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
6456a800f36SLiu Yu 
6466a800f36SLiu Yu 	current->thread.evr[fc] = vc.wp[0];
6476a800f36SLiu Yu 	regs->gpr[fc] = vc.wp[1];
6486a800f36SLiu Yu 
6496a800f36SLiu Yu #ifdef DEBUG
6506a800f36SLiu Yu 	printk("ccr = %08lx\n", regs->ccr);
6516a800f36SLiu Yu 	printk("cur exceptions = %08x spefscr = %08lx\n",
6526a800f36SLiu Yu 			FP_CUR_EXCEPTIONS, __FPU_FPSCR);
6536a800f36SLiu Yu 	printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
6546a800f36SLiu Yu 	printk("va: %08x  %08x\n", va.wp[0], va.wp[1]);
6556a800f36SLiu Yu 	printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
6566a800f36SLiu Yu #endif
6576a800f36SLiu Yu 
6586a800f36SLiu Yu 	return 0;
6596a800f36SLiu Yu 
6606a800f36SLiu Yu illegal:
661ac6f1203SLiu Yu 	if (have_e500_cpu_a005_erratum) {
662ac6f1203SLiu Yu 		/* according to e500 cpu a005 erratum, reissue efp inst */
663ac6f1203SLiu Yu 		regs->nip -= 4;
664ac6f1203SLiu Yu #ifdef DEBUG
665ac6f1203SLiu Yu 		printk(KERN_DEBUG "re-issue efp inst: %08lx\n", speinsn);
666ac6f1203SLiu Yu #endif
667ac6f1203SLiu Yu 		return 0;
668ac6f1203SLiu Yu 	}
669ac6f1203SLiu Yu 
6706a800f36SLiu Yu 	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
6716a800f36SLiu Yu 	return -ENOSYS;
6726a800f36SLiu Yu }
6736a800f36SLiu Yu 
6746a800f36SLiu Yu int speround_handler(struct pt_regs *regs)
6756a800f36SLiu Yu {
6766a800f36SLiu Yu 	union dw_union fgpr;
6776a800f36SLiu Yu 	int s_lo, s_hi;
6786a800f36SLiu Yu 	unsigned long speinsn, type, fc;
6796a800f36SLiu Yu 
6806a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
6816a800f36SLiu Yu 		return -EFAULT;
6826a800f36SLiu Yu 	if ((speinsn >> 26) != 4)
6836a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
6846a800f36SLiu Yu 
6856a800f36SLiu Yu 	type = insn_type(speinsn & 0x7ff);
6866a800f36SLiu Yu 	if (type == XCR) return -ENOSYS;
6876a800f36SLiu Yu 
6886a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
6896a800f36SLiu Yu 	s_lo = regs->gpr[fc] & SIGN_BIT_S;
6906a800f36SLiu Yu 	s_hi = current->thread.evr[fc] & SIGN_BIT_S;
6916a800f36SLiu Yu 	fgpr.wp[0] = current->thread.evr[fc];
6926a800f36SLiu Yu 	fgpr.wp[1] = regs->gpr[fc];
6936a800f36SLiu Yu 
6946a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
6956a800f36SLiu Yu 
6966a800f36SLiu Yu 	switch ((speinsn >> 5) & 0x7) {
6976a800f36SLiu Yu 	/* Since SPE instructions on E500 core can handle round to nearest
6986a800f36SLiu Yu 	 * and round toward zero with IEEE-754 complied, we just need
6996a800f36SLiu Yu 	 * to handle round toward +Inf and round toward -Inf by software.
7006a800f36SLiu Yu 	 */
7016a800f36SLiu Yu 	case SPFP:
7026a800f36SLiu Yu 		if ((FP_ROUNDMODE) == FP_RND_PINF) {
7036a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
7046a800f36SLiu Yu 		} else { /* round to -Inf */
7056a800f36SLiu Yu 			if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */
7066a800f36SLiu Yu 		}
7076a800f36SLiu Yu 		break;
7086a800f36SLiu Yu 
7096a800f36SLiu Yu 	case DPFP:
7106a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
7116a800f36SLiu Yu 			if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */
7126a800f36SLiu Yu 		} else { /* round to -Inf */
7136a800f36SLiu Yu 			if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */
7146a800f36SLiu Yu 		}
7156a800f36SLiu Yu 		break;
7166a800f36SLiu Yu 
7176a800f36SLiu Yu 	case VCT:
7186a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
7196a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
7206a800f36SLiu Yu 			if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
7216a800f36SLiu Yu 		} else { /* round to -Inf */
7226a800f36SLiu Yu 			if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
7236a800f36SLiu Yu 			if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
7246a800f36SLiu Yu 		}
7256a800f36SLiu Yu 		break;
7266a800f36SLiu Yu 
7276a800f36SLiu Yu 	default:
7286a800f36SLiu Yu 		return -EINVAL;
7296a800f36SLiu Yu 	}
7306a800f36SLiu Yu 
7316a800f36SLiu Yu 	current->thread.evr[fc] = fgpr.wp[0];
7326a800f36SLiu Yu 	regs->gpr[fc] = fgpr.wp[1];
7336a800f36SLiu Yu 
7346a800f36SLiu Yu 	return 0;
7356a800f36SLiu Yu }
736ac6f1203SLiu Yu 
737ac6f1203SLiu Yu int __init spe_mathemu_init(void)
738ac6f1203SLiu Yu {
739ac6f1203SLiu Yu 	u32 pvr, maj, min;
740ac6f1203SLiu Yu 
741ac6f1203SLiu Yu 	pvr = mfspr(SPRN_PVR);
742ac6f1203SLiu Yu 
743ac6f1203SLiu Yu 	if ((PVR_VER(pvr) == PVR_VER_E500V1) ||
744ac6f1203SLiu Yu 	    (PVR_VER(pvr) == PVR_VER_E500V2)) {
745ac6f1203SLiu Yu 		maj = PVR_MAJ(pvr);
746ac6f1203SLiu Yu 		min = PVR_MIN(pvr);
747ac6f1203SLiu Yu 
748ac6f1203SLiu Yu 		/*
749ac6f1203SLiu Yu 		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1
750ac6f1203SLiu Yu 		 * need cpu a005 errata workaround
751ac6f1203SLiu Yu 		 */
752ac6f1203SLiu Yu 		switch (maj) {
753ac6f1203SLiu Yu 		case 1:
754ac6f1203SLiu Yu 			if (min < 1)
755ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
756ac6f1203SLiu Yu 			break;
757ac6f1203SLiu Yu 		case 2:
758ac6f1203SLiu Yu 			if (min < 3)
759ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
760ac6f1203SLiu Yu 			break;
761ac6f1203SLiu Yu 		case 3:
762ac6f1203SLiu Yu 		case 4:
763ac6f1203SLiu Yu 		case 5:
764ac6f1203SLiu Yu 			if (min < 1)
765ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
766ac6f1203SLiu Yu 			break;
767ac6f1203SLiu Yu 		default:
768ac6f1203SLiu Yu 			break;
769ac6f1203SLiu Yu 		}
770ac6f1203SLiu Yu 	}
771ac6f1203SLiu Yu 
772ac6f1203SLiu Yu 	return 0;
773ac6f1203SLiu Yu }
774ac6f1203SLiu Yu 
775ac6f1203SLiu Yu module_init(spe_mathemu_init);
776