xref: /linux/arch/powerpc/math-emu/math_efp.c (revision b430abc4d10537992a5ba38eff6c46b3c9009a19)
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 
215*b430abc4SLiu Yu 	pr_debug("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
216*b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
217*b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
218*b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
2196a800f36SLiu Yu 
2206a800f36SLiu Yu 	switch (src) {
2216a800f36SLiu Yu 	case SPFP: {
2226a800f36SLiu Yu 		FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
2236a800f36SLiu Yu 
2246a800f36SLiu Yu 		switch (type) {
2256a800f36SLiu Yu 		case AB:
2266a800f36SLiu Yu 		case XCR:
2276a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2286a800f36SLiu Yu 		case XB:
2296a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
2306a800f36SLiu Yu 			break;
2316a800f36SLiu Yu 		case XA:
2326a800f36SLiu Yu 			FP_UNPACK_SP(SA, va.wp + 1);
2336a800f36SLiu Yu 			break;
2346a800f36SLiu Yu 		}
2356a800f36SLiu Yu 
236*b430abc4SLiu Yu 		pr_debug("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c);
237*b430abc4SLiu Yu 		pr_debug("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);
2386a800f36SLiu Yu 
2396a800f36SLiu Yu 		switch (func) {
2406a800f36SLiu Yu 		case EFSABS:
2416a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
2426a800f36SLiu Yu 			goto update_regs;
2436a800f36SLiu Yu 
2446a800f36SLiu Yu 		case EFSNABS:
2456a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
2466a800f36SLiu Yu 			goto update_regs;
2476a800f36SLiu Yu 
2486a800f36SLiu Yu 		case EFSNEG:
2496a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
2506a800f36SLiu Yu 			goto update_regs;
2516a800f36SLiu Yu 
2526a800f36SLiu Yu 		case EFSADD:
2536a800f36SLiu Yu 			FP_ADD_S(SR, SA, SB);
2546a800f36SLiu Yu 			goto pack_s;
2556a800f36SLiu Yu 
2566a800f36SLiu Yu 		case EFSSUB:
2576a800f36SLiu Yu 			FP_SUB_S(SR, SA, SB);
2586a800f36SLiu Yu 			goto pack_s;
2596a800f36SLiu Yu 
2606a800f36SLiu Yu 		case EFSMUL:
2616a800f36SLiu Yu 			FP_MUL_S(SR, SA, SB);
2626a800f36SLiu Yu 			goto pack_s;
2636a800f36SLiu Yu 
2646a800f36SLiu Yu 		case EFSDIV:
2656a800f36SLiu Yu 			FP_DIV_S(SR, SA, SB);
2666a800f36SLiu Yu 			goto pack_s;
2676a800f36SLiu Yu 
2686a800f36SLiu Yu 		case EFSCMPEQ:
2696a800f36SLiu Yu 			cmp = 0;
2706a800f36SLiu Yu 			goto cmp_s;
2716a800f36SLiu Yu 
2726a800f36SLiu Yu 		case EFSCMPGT:
2736a800f36SLiu Yu 			cmp = 1;
2746a800f36SLiu Yu 			goto cmp_s;
2756a800f36SLiu Yu 
2766a800f36SLiu Yu 		case EFSCMPLT:
2776a800f36SLiu Yu 			cmp = -1;
2786a800f36SLiu Yu 			goto cmp_s;
2796a800f36SLiu Yu 
2806a800f36SLiu Yu 		case EFSCTSF:
2816a800f36SLiu Yu 		case EFSCTUF:
2826a800f36SLiu Yu 			if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) {
2836a800f36SLiu Yu 				/* NaN */
2846a800f36SLiu Yu 				if (((vb.wp[1] >> 23) & 0xff) == 0) {
2856a800f36SLiu Yu 					/* denorm */
2866a800f36SLiu Yu 					vc.wp[1] = 0x0;
2876a800f36SLiu Yu 				} else if ((vb.wp[1] >> 31) == 0) {
2886a800f36SLiu Yu 					/* positive normal */
2896a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2906a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
2916a800f36SLiu Yu 				} else { /* negative normal */
2926a800f36SLiu Yu 					vc.wp[1] = (func == EFSCTSF) ?
2936a800f36SLiu Yu 						0x80000000 : 0x0;
2946a800f36SLiu Yu 				}
2956a800f36SLiu Yu 			} else { /* rB is NaN */
2966a800f36SLiu Yu 				vc.wp[1] = 0x0;
2976a800f36SLiu Yu 			}
2986a800f36SLiu Yu 			goto update_regs;
2996a800f36SLiu Yu 
3006a800f36SLiu Yu 		case EFSCFD: {
3016a800f36SLiu Yu 			FP_DECL_D(DB);
3026a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
3036a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
304*b430abc4SLiu Yu 
305*b430abc4SLiu Yu 			pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
3066a800f36SLiu Yu 					DB_s, DB_f1, DB_f0, DB_e, DB_c);
307*b430abc4SLiu Yu 
3086a800f36SLiu Yu 			FP_CONV(S, D, 1, 2, SR, DB);
3096a800f36SLiu Yu 			goto pack_s;
3106a800f36SLiu Yu 		}
3116a800f36SLiu Yu 
3126a800f36SLiu Yu 		case EFSCTSI:
3136a800f36SLiu Yu 		case EFSCTSIZ:
3146a800f36SLiu Yu 		case EFSCTUI:
3156a800f36SLiu Yu 		case EFSCTUIZ:
3166a800f36SLiu Yu 			if (func & 0x4) {
3176a800f36SLiu Yu 				_FP_ROUND(1, SB);
3186a800f36SLiu Yu 			} else {
3196a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB);
3206a800f36SLiu Yu 			}
321afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB, 32,
322afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB_s));
3236a800f36SLiu Yu 			goto update_regs;
3246a800f36SLiu Yu 
3256a800f36SLiu Yu 		default:
3266a800f36SLiu Yu 			goto illegal;
3276a800f36SLiu Yu 		}
3286a800f36SLiu Yu 		break;
3296a800f36SLiu Yu 
3306a800f36SLiu Yu pack_s:
331*b430abc4SLiu Yu 		pr_debug("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c);
332*b430abc4SLiu Yu 
3336a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR);
3346a800f36SLiu Yu 		goto update_regs;
3356a800f36SLiu Yu 
3366a800f36SLiu Yu cmp_s:
3376a800f36SLiu Yu 		FP_CMP_S(IR, SA, SB, 3);
3386a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB)))
3396a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
3406a800f36SLiu Yu 		if (IR == cmp) {
3416a800f36SLiu Yu 			IR = 0x4;
3426a800f36SLiu Yu 		} else {
3436a800f36SLiu Yu 			IR = 0;
3446a800f36SLiu Yu 		}
3456a800f36SLiu Yu 		goto update_ccr;
3466a800f36SLiu Yu 	}
3476a800f36SLiu Yu 
3486a800f36SLiu Yu 	case DPFP: {
3496a800f36SLiu Yu 		FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
3506a800f36SLiu Yu 
3516a800f36SLiu Yu 		switch (type) {
3526a800f36SLiu Yu 		case AB:
3536a800f36SLiu Yu 		case XCR:
3546a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3556a800f36SLiu Yu 		case XB:
3566a800f36SLiu Yu 			FP_UNPACK_DP(DB, vb.dp);
3576a800f36SLiu Yu 			break;
3586a800f36SLiu Yu 		case XA:
3596a800f36SLiu Yu 			FP_UNPACK_DP(DA, va.dp);
3606a800f36SLiu Yu 			break;
3616a800f36SLiu Yu 		}
3626a800f36SLiu Yu 
363*b430abc4SLiu Yu 		pr_debug("DA: %ld %08lx %08lx %ld (%ld)\n",
3646a800f36SLiu Yu 				DA_s, DA_f1, DA_f0, DA_e, DA_c);
365*b430abc4SLiu Yu 		pr_debug("DB: %ld %08lx %08lx %ld (%ld)\n",
3666a800f36SLiu Yu 				DB_s, DB_f1, DB_f0, DB_e, DB_c);
3676a800f36SLiu Yu 
3686a800f36SLiu Yu 		switch (func) {
3696a800f36SLiu Yu 		case EFDABS:
3706a800f36SLiu Yu 			vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
3716a800f36SLiu Yu 			goto update_regs;
3726a800f36SLiu Yu 
3736a800f36SLiu Yu 		case EFDNABS:
3746a800f36SLiu Yu 			vc.dp[0] = va.dp[0] | SIGN_BIT_D;
3756a800f36SLiu Yu 			goto update_regs;
3766a800f36SLiu Yu 
3776a800f36SLiu Yu 		case EFDNEG:
3786a800f36SLiu Yu 			vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
3796a800f36SLiu Yu 			goto update_regs;
3806a800f36SLiu Yu 
3816a800f36SLiu Yu 		case EFDADD:
3826a800f36SLiu Yu 			FP_ADD_D(DR, DA, DB);
3836a800f36SLiu Yu 			goto pack_d;
3846a800f36SLiu Yu 
3856a800f36SLiu Yu 		case EFDSUB:
3866a800f36SLiu Yu 			FP_SUB_D(DR, DA, DB);
3876a800f36SLiu Yu 			goto pack_d;
3886a800f36SLiu Yu 
3896a800f36SLiu Yu 		case EFDMUL:
3906a800f36SLiu Yu 			FP_MUL_D(DR, DA, DB);
3916a800f36SLiu Yu 			goto pack_d;
3926a800f36SLiu Yu 
3936a800f36SLiu Yu 		case EFDDIV:
3946a800f36SLiu Yu 			FP_DIV_D(DR, DA, DB);
3956a800f36SLiu Yu 			goto pack_d;
3966a800f36SLiu Yu 
3976a800f36SLiu Yu 		case EFDCMPEQ:
3986a800f36SLiu Yu 			cmp = 0;
3996a800f36SLiu Yu 			goto cmp_d;
4006a800f36SLiu Yu 
4016a800f36SLiu Yu 		case EFDCMPGT:
4026a800f36SLiu Yu 			cmp = 1;
4036a800f36SLiu Yu 			goto cmp_d;
4046a800f36SLiu Yu 
4056a800f36SLiu Yu 		case EFDCMPLT:
4066a800f36SLiu Yu 			cmp = -1;
4076a800f36SLiu Yu 			goto cmp_d;
4086a800f36SLiu Yu 
4096a800f36SLiu Yu 		case EFDCTSF:
4106a800f36SLiu Yu 		case EFDCTUF:
4116a800f36SLiu Yu 			if (!((vb.wp[0] >> 20) == 0x7ff &&
4126a800f36SLiu Yu 			   ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) {
4136a800f36SLiu Yu 				/* not a NaN */
4146a800f36SLiu Yu 				if (((vb.wp[0] >> 20) & 0x7ff) == 0) {
4156a800f36SLiu Yu 					/* denorm */
4166a800f36SLiu Yu 					vc.wp[1] = 0x0;
4176a800f36SLiu Yu 				} else if ((vb.wp[0] >> 31) == 0) {
4186a800f36SLiu Yu 					/* positive normal */
4196a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4206a800f36SLiu Yu 						0x7fffffff : 0xffffffff;
4216a800f36SLiu Yu 				} else { /* negative normal */
4226a800f36SLiu Yu 					vc.wp[1] = (func == EFDCTSF) ?
4236a800f36SLiu Yu 						0x80000000 : 0x0;
4246a800f36SLiu Yu 				}
4256a800f36SLiu Yu 			} else { /* NaN */
4266a800f36SLiu Yu 				vc.wp[1] = 0x0;
4276a800f36SLiu Yu 			}
4286a800f36SLiu Yu 			goto update_regs;
4296a800f36SLiu Yu 
4306a800f36SLiu Yu 		case EFDCFS: {
4316a800f36SLiu Yu 			FP_DECL_S(SB);
4326a800f36SLiu Yu 			FP_CLEAR_EXCEPTIONS;
4336a800f36SLiu Yu 			FP_UNPACK_SP(SB, vb.wp + 1);
434*b430abc4SLiu Yu 
435*b430abc4SLiu Yu 			pr_debug("SB: %ld %08lx %ld (%ld)\n",
4366a800f36SLiu Yu 					SB_s, SB_f, SB_e, SB_c);
437*b430abc4SLiu Yu 
4386a800f36SLiu Yu 			FP_CONV(D, S, 2, 1, DR, SB);
4396a800f36SLiu Yu 			goto pack_d;
4406a800f36SLiu Yu 		}
4416a800f36SLiu Yu 
4426a800f36SLiu Yu 		case EFDCTUIDZ:
4436a800f36SLiu Yu 		case EFDCTSIDZ:
4446a800f36SLiu Yu 			_FP_ROUND_ZERO(2, DB);
4456a800f36SLiu Yu 			FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0));
4466a800f36SLiu Yu 			goto update_regs;
4476a800f36SLiu Yu 
4486a800f36SLiu Yu 		case EFDCTUI:
4496a800f36SLiu Yu 		case EFDCTSI:
4506a800f36SLiu Yu 		case EFDCTUIZ:
4516a800f36SLiu Yu 		case EFDCTSIZ:
4526a800f36SLiu Yu 			if (func & 0x4) {
4536a800f36SLiu Yu 				_FP_ROUND(2, DB);
4546a800f36SLiu Yu 			} else {
4556a800f36SLiu Yu 				_FP_ROUND_ZERO(2, DB);
4566a800f36SLiu Yu 			}
457afc0a07dSShan Hai 			FP_TO_INT_D(vc.wp[1], DB, 32,
458afc0a07dSShan Hai 					(((func & 0x3) != 0) || DB_s));
4596a800f36SLiu Yu 			goto update_regs;
4606a800f36SLiu Yu 
4616a800f36SLiu Yu 		default:
4626a800f36SLiu Yu 			goto illegal;
4636a800f36SLiu Yu 		}
4646a800f36SLiu Yu 		break;
4656a800f36SLiu Yu 
4666a800f36SLiu Yu pack_d:
467*b430abc4SLiu Yu 		pr_debug("DR: %ld %08lx %08lx %ld (%ld)\n",
4686a800f36SLiu Yu 				DR_s, DR_f1, DR_f0, DR_e, DR_c);
469*b430abc4SLiu Yu 
4706a800f36SLiu Yu 		FP_PACK_DP(vc.dp, DR);
4716a800f36SLiu Yu 		goto update_regs;
4726a800f36SLiu Yu 
4736a800f36SLiu Yu cmp_d:
4746a800f36SLiu Yu 		FP_CMP_D(IR, DA, DB, 3);
4756a800f36SLiu Yu 		if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
4766a800f36SLiu Yu 			FP_SET_EXCEPTION(FP_EX_INVALID);
4776a800f36SLiu Yu 		if (IR == cmp) {
4786a800f36SLiu Yu 			IR = 0x4;
4796a800f36SLiu Yu 		} else {
4806a800f36SLiu Yu 			IR = 0;
4816a800f36SLiu Yu 		}
4826a800f36SLiu Yu 		goto update_ccr;
4836a800f36SLiu Yu 
4846a800f36SLiu Yu 	}
4856a800f36SLiu Yu 
4866a800f36SLiu Yu 	case VCT: {
4876a800f36SLiu Yu 		FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
4886a800f36SLiu Yu 		FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
4896a800f36SLiu Yu 		int IR0, IR1;
4906a800f36SLiu Yu 
4916a800f36SLiu Yu 		switch (type) {
4926a800f36SLiu Yu 		case AB:
4936a800f36SLiu Yu 		case XCR:
4946a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
4956a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
4966a800f36SLiu Yu 		case XB:
4976a800f36SLiu Yu 			FP_UNPACK_SP(SB0, vb.wp);
4986a800f36SLiu Yu 			FP_UNPACK_SP(SB1, vb.wp + 1);
4996a800f36SLiu Yu 			break;
5006a800f36SLiu Yu 		case XA:
5016a800f36SLiu Yu 			FP_UNPACK_SP(SA0, va.wp);
5026a800f36SLiu Yu 			FP_UNPACK_SP(SA1, va.wp + 1);
5036a800f36SLiu Yu 			break;
5046a800f36SLiu Yu 		}
5056a800f36SLiu Yu 
506*b430abc4SLiu Yu 		pr_debug("SA0: %ld %08lx %ld (%ld)\n",
507*b430abc4SLiu Yu 				SA0_s, SA0_f, SA0_e, SA0_c);
508*b430abc4SLiu Yu 		pr_debug("SA1: %ld %08lx %ld (%ld)\n",
509*b430abc4SLiu Yu 				SA1_s, SA1_f, SA1_e, SA1_c);
510*b430abc4SLiu Yu 		pr_debug("SB0: %ld %08lx %ld (%ld)\n",
511*b430abc4SLiu Yu 				SB0_s, SB0_f, SB0_e, SB0_c);
512*b430abc4SLiu Yu 		pr_debug("SB1: %ld %08lx %ld (%ld)\n",
513*b430abc4SLiu Yu 				SB1_s, SB1_f, SB1_e, SB1_c);
5146a800f36SLiu Yu 
5156a800f36SLiu Yu 		switch (func) {
5166a800f36SLiu Yu 		case EVFSABS:
5176a800f36SLiu Yu 			vc.wp[0] = va.wp[0] & ~SIGN_BIT_S;
5186a800f36SLiu Yu 			vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
5196a800f36SLiu Yu 			goto update_regs;
5206a800f36SLiu Yu 
5216a800f36SLiu Yu 		case EVFSNABS:
5226a800f36SLiu Yu 			vc.wp[0] = va.wp[0] | SIGN_BIT_S;
5236a800f36SLiu Yu 			vc.wp[1] = va.wp[1] | SIGN_BIT_S;
5246a800f36SLiu Yu 			goto update_regs;
5256a800f36SLiu Yu 
5266a800f36SLiu Yu 		case EVFSNEG:
5276a800f36SLiu Yu 			vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
5286a800f36SLiu Yu 			vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
5296a800f36SLiu Yu 			goto update_regs;
5306a800f36SLiu Yu 
5316a800f36SLiu Yu 		case EVFSADD:
5326a800f36SLiu Yu 			FP_ADD_S(SR0, SA0, SB0);
5336a800f36SLiu Yu 			FP_ADD_S(SR1, SA1, SB1);
5346a800f36SLiu Yu 			goto pack_vs;
5356a800f36SLiu Yu 
5366a800f36SLiu Yu 		case EVFSSUB:
5376a800f36SLiu Yu 			FP_SUB_S(SR0, SA0, SB0);
5386a800f36SLiu Yu 			FP_SUB_S(SR1, SA1, SB1);
5396a800f36SLiu Yu 			goto pack_vs;
5406a800f36SLiu Yu 
5416a800f36SLiu Yu 		case EVFSMUL:
5426a800f36SLiu Yu 			FP_MUL_S(SR0, SA0, SB0);
5436a800f36SLiu Yu 			FP_MUL_S(SR1, SA1, SB1);
5446a800f36SLiu Yu 			goto pack_vs;
5456a800f36SLiu Yu 
5466a800f36SLiu Yu 		case EVFSDIV:
5476a800f36SLiu Yu 			FP_DIV_S(SR0, SA0, SB0);
5486a800f36SLiu Yu 			FP_DIV_S(SR1, SA1, SB1);
5496a800f36SLiu Yu 			goto pack_vs;
5506a800f36SLiu Yu 
5516a800f36SLiu Yu 		case EVFSCMPEQ:
5526a800f36SLiu Yu 			cmp = 0;
5536a800f36SLiu Yu 			goto cmp_vs;
5546a800f36SLiu Yu 
5556a800f36SLiu Yu 		case EVFSCMPGT:
5566a800f36SLiu Yu 			cmp = 1;
5576a800f36SLiu Yu 			goto cmp_vs;
5586a800f36SLiu Yu 
5596a800f36SLiu Yu 		case EVFSCMPLT:
5606a800f36SLiu Yu 			cmp = -1;
5616a800f36SLiu Yu 			goto cmp_vs;
5626a800f36SLiu Yu 
5636a800f36SLiu Yu 		case EVFSCTSF:
5646a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5656a800f36SLiu Yu 				"efsctsf %0, %2\n"
5666a800f36SLiu Yu 				"efsctsf %1, %3\n"
5676a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5686a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5696a800f36SLiu Yu 			goto update_regs;
5706a800f36SLiu Yu 
5716a800f36SLiu Yu 		case EVFSCTUF:
5726a800f36SLiu Yu 			__asm__ __volatile__ ("mtspr 512, %4\n"
5736a800f36SLiu Yu 				"efsctuf %0, %2\n"
5746a800f36SLiu Yu 				"efsctuf %1, %3\n"
5756a800f36SLiu Yu 				: "=r" (vc.wp[0]), "=r" (vc.wp[1])
5766a800f36SLiu Yu 				: "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
5776a800f36SLiu Yu 			goto update_regs;
5786a800f36SLiu Yu 
5796a800f36SLiu Yu 		case EVFSCTUI:
5806a800f36SLiu Yu 		case EVFSCTSI:
5816a800f36SLiu Yu 		case EVFSCTUIZ:
5826a800f36SLiu Yu 		case EVFSCTSIZ:
5836a800f36SLiu Yu 			if (func & 0x4) {
5846a800f36SLiu Yu 				_FP_ROUND(1, SB0);
5856a800f36SLiu Yu 				_FP_ROUND(1, SB1);
5866a800f36SLiu Yu 			} else {
5876a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB0);
5886a800f36SLiu Yu 				_FP_ROUND_ZERO(1, SB1);
5896a800f36SLiu Yu 			}
590afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[0], SB0, 32,
591afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB0_s));
592afc0a07dSShan Hai 			FP_TO_INT_S(vc.wp[1], SB1, 32,
593afc0a07dSShan Hai 					(((func & 0x3) != 0) || SB1_s));
5946a800f36SLiu Yu 			goto update_regs;
5956a800f36SLiu Yu 
5966a800f36SLiu Yu 		default:
5976a800f36SLiu Yu 			goto illegal;
5986a800f36SLiu Yu 		}
5996a800f36SLiu Yu 		break;
6006a800f36SLiu Yu 
6016a800f36SLiu Yu pack_vs:
602*b430abc4SLiu Yu 		pr_debug("SR0: %ld %08lx %ld (%ld)\n",
603*b430abc4SLiu Yu 				SR0_s, SR0_f, SR0_e, SR0_c);
604*b430abc4SLiu Yu 		pr_debug("SR1: %ld %08lx %ld (%ld)\n",
605*b430abc4SLiu Yu 				SR1_s, SR1_f, SR1_e, SR1_c);
606*b430abc4SLiu Yu 
6076a800f36SLiu Yu 		FP_PACK_SP(vc.wp, SR0);
6086a800f36SLiu Yu 		FP_PACK_SP(vc.wp + 1, SR1);
6096a800f36SLiu Yu 		goto update_regs;
6106a800f36SLiu Yu 
6116a800f36SLiu Yu cmp_vs:
6126a800f36SLiu Yu 		{
6136a800f36SLiu Yu 			int ch, cl;
6146a800f36SLiu Yu 
6156a800f36SLiu Yu 			FP_CMP_S(IR0, SA0, SB0, 3);
6166a800f36SLiu Yu 			FP_CMP_S(IR1, SA1, SB1, 3);
6176a800f36SLiu Yu 			if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
6186a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6196a800f36SLiu Yu 			if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
6206a800f36SLiu Yu 				FP_SET_EXCEPTION(FP_EX_INVALID);
6216a800f36SLiu Yu 			ch = (IR0 == cmp) ? 1 : 0;
6226a800f36SLiu Yu 			cl = (IR1 == cmp) ? 1 : 0;
6236a800f36SLiu Yu 			IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
6246a800f36SLiu Yu 				((ch & cl) << 0);
6256a800f36SLiu Yu 			goto update_ccr;
6266a800f36SLiu Yu 		}
6276a800f36SLiu Yu 	}
6286a800f36SLiu Yu 	default:
6296a800f36SLiu Yu 		return -EINVAL;
6306a800f36SLiu Yu 	}
6316a800f36SLiu Yu 
6326a800f36SLiu Yu update_ccr:
6336a800f36SLiu Yu 	regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6346a800f36SLiu Yu 	regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
6356a800f36SLiu Yu 
6366a800f36SLiu Yu update_regs:
6376a800f36SLiu Yu 	__FPU_FPSCR &= ~FP_EX_MASK;
6386a800f36SLiu Yu 	__FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
6396a800f36SLiu Yu 	mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
6406a800f36SLiu Yu 
6416a800f36SLiu Yu 	current->thread.evr[fc] = vc.wp[0];
6426a800f36SLiu Yu 	regs->gpr[fc] = vc.wp[1];
6436a800f36SLiu Yu 
644*b430abc4SLiu Yu 	pr_debug("ccr = %08lx\n", regs->ccr);
645*b430abc4SLiu Yu 	pr_debug("cur exceptions = %08x spefscr = %08lx\n",
6466a800f36SLiu Yu 			FP_CUR_EXCEPTIONS, __FPU_FPSCR);
647*b430abc4SLiu Yu 	pr_debug("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]);
648*b430abc4SLiu Yu 	pr_debug("va: %08x  %08x\n", va.wp[0], va.wp[1]);
649*b430abc4SLiu Yu 	pr_debug("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]);
6506a800f36SLiu Yu 
6516a800f36SLiu Yu 	return 0;
6526a800f36SLiu Yu 
6536a800f36SLiu Yu illegal:
654ac6f1203SLiu Yu 	if (have_e500_cpu_a005_erratum) {
655ac6f1203SLiu Yu 		/* according to e500 cpu a005 erratum, reissue efp inst */
656ac6f1203SLiu Yu 		regs->nip -= 4;
657*b430abc4SLiu Yu 		pr_debug("re-issue efp inst: %08lx\n", speinsn);
658ac6f1203SLiu Yu 		return 0;
659ac6f1203SLiu Yu 	}
660ac6f1203SLiu Yu 
6616a800f36SLiu Yu 	printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
6626a800f36SLiu Yu 	return -ENOSYS;
6636a800f36SLiu Yu }
6646a800f36SLiu Yu 
6656a800f36SLiu Yu int speround_handler(struct pt_regs *regs)
6666a800f36SLiu Yu {
6676a800f36SLiu Yu 	union dw_union fgpr;
6686a800f36SLiu Yu 	int s_lo, s_hi;
6696a800f36SLiu Yu 	unsigned long speinsn, type, fc;
6706a800f36SLiu Yu 
6716a800f36SLiu Yu 	if (get_user(speinsn, (unsigned int __user *) regs->nip))
6726a800f36SLiu Yu 		return -EFAULT;
6736a800f36SLiu Yu 	if ((speinsn >> 26) != 4)
6746a800f36SLiu Yu 		return -EINVAL;         /* not an spe instruction */
6756a800f36SLiu Yu 
6766a800f36SLiu Yu 	type = insn_type(speinsn & 0x7ff);
6776a800f36SLiu Yu 	if (type == XCR) return -ENOSYS;
6786a800f36SLiu Yu 
6796a800f36SLiu Yu 	fc = (speinsn >> 21) & 0x1f;
6806a800f36SLiu Yu 	s_lo = regs->gpr[fc] & SIGN_BIT_S;
6816a800f36SLiu Yu 	s_hi = current->thread.evr[fc] & SIGN_BIT_S;
6826a800f36SLiu Yu 	fgpr.wp[0] = current->thread.evr[fc];
6836a800f36SLiu Yu 	fgpr.wp[1] = regs->gpr[fc];
6846a800f36SLiu Yu 
6856a800f36SLiu Yu 	__FPU_FPSCR = mfspr(SPRN_SPEFSCR);
6866a800f36SLiu Yu 
6876a800f36SLiu Yu 	switch ((speinsn >> 5) & 0x7) {
6886a800f36SLiu Yu 	/* Since SPE instructions on E500 core can handle round to nearest
6896a800f36SLiu Yu 	 * and round toward zero with IEEE-754 complied, we just need
6906a800f36SLiu Yu 	 * to handle round toward +Inf and round toward -Inf by software.
6916a800f36SLiu Yu 	 */
6926a800f36SLiu Yu 	case SPFP:
6936a800f36SLiu Yu 		if ((FP_ROUNDMODE) == FP_RND_PINF) {
6946a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
6956a800f36SLiu Yu 		} else { /* round to -Inf */
6966a800f36SLiu Yu 			if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */
6976a800f36SLiu Yu 		}
6986a800f36SLiu Yu 		break;
6996a800f36SLiu Yu 
7006a800f36SLiu Yu 	case DPFP:
7016a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
7026a800f36SLiu Yu 			if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */
7036a800f36SLiu Yu 		} else { /* round to -Inf */
7046a800f36SLiu Yu 			if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */
7056a800f36SLiu Yu 		}
7066a800f36SLiu Yu 		break;
7076a800f36SLiu Yu 
7086a800f36SLiu Yu 	case VCT:
7096a800f36SLiu Yu 		if (FP_ROUNDMODE == FP_RND_PINF) {
7106a800f36SLiu Yu 			if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
7116a800f36SLiu Yu 			if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
7126a800f36SLiu Yu 		} else { /* round to -Inf */
7136a800f36SLiu Yu 			if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
7146a800f36SLiu Yu 			if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
7156a800f36SLiu Yu 		}
7166a800f36SLiu Yu 		break;
7176a800f36SLiu Yu 
7186a800f36SLiu Yu 	default:
7196a800f36SLiu Yu 		return -EINVAL;
7206a800f36SLiu Yu 	}
7216a800f36SLiu Yu 
7226a800f36SLiu Yu 	current->thread.evr[fc] = fgpr.wp[0];
7236a800f36SLiu Yu 	regs->gpr[fc] = fgpr.wp[1];
7246a800f36SLiu Yu 
7256a800f36SLiu Yu 	return 0;
7266a800f36SLiu Yu }
727ac6f1203SLiu Yu 
728ac6f1203SLiu Yu int __init spe_mathemu_init(void)
729ac6f1203SLiu Yu {
730ac6f1203SLiu Yu 	u32 pvr, maj, min;
731ac6f1203SLiu Yu 
732ac6f1203SLiu Yu 	pvr = mfspr(SPRN_PVR);
733ac6f1203SLiu Yu 
734ac6f1203SLiu Yu 	if ((PVR_VER(pvr) == PVR_VER_E500V1) ||
735ac6f1203SLiu Yu 	    (PVR_VER(pvr) == PVR_VER_E500V2)) {
736ac6f1203SLiu Yu 		maj = PVR_MAJ(pvr);
737ac6f1203SLiu Yu 		min = PVR_MIN(pvr);
738ac6f1203SLiu Yu 
739ac6f1203SLiu Yu 		/*
740ac6f1203SLiu Yu 		 * E500 revision below 1.1, 2.3, 3.1, 4.1, 5.1
741ac6f1203SLiu Yu 		 * need cpu a005 errata workaround
742ac6f1203SLiu Yu 		 */
743ac6f1203SLiu Yu 		switch (maj) {
744ac6f1203SLiu Yu 		case 1:
745ac6f1203SLiu Yu 			if (min < 1)
746ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
747ac6f1203SLiu Yu 			break;
748ac6f1203SLiu Yu 		case 2:
749ac6f1203SLiu Yu 			if (min < 3)
750ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
751ac6f1203SLiu Yu 			break;
752ac6f1203SLiu Yu 		case 3:
753ac6f1203SLiu Yu 		case 4:
754ac6f1203SLiu Yu 		case 5:
755ac6f1203SLiu Yu 			if (min < 1)
756ac6f1203SLiu Yu 				have_e500_cpu_a005_erratum = 1;
757ac6f1203SLiu Yu 			break;
758ac6f1203SLiu Yu 		default:
759ac6f1203SLiu Yu 			break;
760ac6f1203SLiu Yu 		}
761ac6f1203SLiu Yu 	}
762ac6f1203SLiu Yu 
763ac6f1203SLiu Yu 	return 0;
764ac6f1203SLiu Yu }
765ac6f1203SLiu Yu 
766ac6f1203SLiu Yu module_init(spe_mathemu_init);
767