|  | /* | 
|  | * arch/powerpc/math-emu/math_efp.c | 
|  | * | 
|  | * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. | 
|  | * | 
|  | * Author: Ebony Zhu,	<ebony.zhu@freescale.com> | 
|  | *         Yu Liu,	<yu.liu@freescale.com> | 
|  | * | 
|  | * Derived from arch/alpha/math-emu/math.c | 
|  | *              arch/powerpc/math-emu/math.c | 
|  | * | 
|  | * Description: | 
|  | * This file is the exception handler to make E500 SPE instructions | 
|  | * fully comply with IEEE-754 floating point standard. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License | 
|  | * as published by the Free Software Foundation; either version | 
|  | * 2 of the License, or (at your option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <linux/types.h> | 
|  |  | 
|  | #include <asm/uaccess.h> | 
|  | #include <asm/reg.h> | 
|  |  | 
|  | #define FP_EX_BOOKE_E500_SPE | 
|  | #include <asm/sfp-machine.h> | 
|  |  | 
|  | #include <math-emu/soft-fp.h> | 
|  | #include <math-emu/single.h> | 
|  | #include <math-emu/double.h> | 
|  |  | 
|  | #define EFAPU		0x4 | 
|  |  | 
|  | #define VCT		0x4 | 
|  | #define SPFP		0x6 | 
|  | #define DPFP		0x7 | 
|  |  | 
|  | #define EFSADD		0x2c0 | 
|  | #define EFSSUB		0x2c1 | 
|  | #define EFSABS		0x2c4 | 
|  | #define EFSNABS		0x2c5 | 
|  | #define EFSNEG		0x2c6 | 
|  | #define EFSMUL		0x2c8 | 
|  | #define EFSDIV		0x2c9 | 
|  | #define EFSCMPGT	0x2cc | 
|  | #define EFSCMPLT	0x2cd | 
|  | #define EFSCMPEQ	0x2ce | 
|  | #define EFSCFD		0x2cf | 
|  | #define EFSCFSI		0x2d1 | 
|  | #define EFSCTUI		0x2d4 | 
|  | #define EFSCTSI		0x2d5 | 
|  | #define EFSCTUF		0x2d6 | 
|  | #define EFSCTSF		0x2d7 | 
|  | #define EFSCTUIZ	0x2d8 | 
|  | #define EFSCTSIZ	0x2da | 
|  |  | 
|  | #define EVFSADD		0x280 | 
|  | #define EVFSSUB		0x281 | 
|  | #define EVFSABS		0x284 | 
|  | #define EVFSNABS	0x285 | 
|  | #define EVFSNEG		0x286 | 
|  | #define EVFSMUL		0x288 | 
|  | #define EVFSDIV		0x289 | 
|  | #define EVFSCMPGT	0x28c | 
|  | #define EVFSCMPLT	0x28d | 
|  | #define EVFSCMPEQ	0x28e | 
|  | #define EVFSCTUI	0x294 | 
|  | #define EVFSCTSI	0x295 | 
|  | #define EVFSCTUF	0x296 | 
|  | #define EVFSCTSF	0x297 | 
|  | #define EVFSCTUIZ	0x298 | 
|  | #define EVFSCTSIZ	0x29a | 
|  |  | 
|  | #define EFDADD		0x2e0 | 
|  | #define EFDSUB		0x2e1 | 
|  | #define EFDABS		0x2e4 | 
|  | #define EFDNABS		0x2e5 | 
|  | #define EFDNEG		0x2e6 | 
|  | #define EFDMUL		0x2e8 | 
|  | #define EFDDIV		0x2e9 | 
|  | #define EFDCTUIDZ	0x2ea | 
|  | #define EFDCTSIDZ	0x2eb | 
|  | #define EFDCMPGT	0x2ec | 
|  | #define EFDCMPLT	0x2ed | 
|  | #define EFDCMPEQ	0x2ee | 
|  | #define EFDCFS		0x2ef | 
|  | #define EFDCTUI		0x2f4 | 
|  | #define EFDCTSI		0x2f5 | 
|  | #define EFDCTUF		0x2f6 | 
|  | #define EFDCTSF		0x2f7 | 
|  | #define EFDCTUIZ	0x2f8 | 
|  | #define EFDCTSIZ	0x2fa | 
|  |  | 
|  | #define AB	2 | 
|  | #define XA	3 | 
|  | #define XB	4 | 
|  | #define XCR	5 | 
|  | #define NOTYPE	0 | 
|  |  | 
|  | #define SIGN_BIT_S	(1UL << 31) | 
|  | #define SIGN_BIT_D	(1ULL << 63) | 
|  | #define FP_EX_MASK	(FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \ | 
|  | FP_EX_UNDERFLOW | FP_EX_OVERFLOW) | 
|  |  | 
|  | union dw_union { | 
|  | u64 dp[1]; | 
|  | u32 wp[2]; | 
|  | }; | 
|  |  | 
|  | static unsigned long insn_type(unsigned long speinsn) | 
|  | { | 
|  | unsigned long ret = NOTYPE; | 
|  |  | 
|  | switch (speinsn & 0x7ff) { | 
|  | case EFSABS:	ret = XA;	break; | 
|  | case EFSADD:	ret = AB;	break; | 
|  | case EFSCFD:	ret = XB;	break; | 
|  | case EFSCMPEQ:	ret = XCR;	break; | 
|  | case EFSCMPGT:	ret = XCR;	break; | 
|  | case EFSCMPLT:	ret = XCR;	break; | 
|  | case EFSCTSF:	ret = XB;	break; | 
|  | case EFSCTSI:	ret = XB;	break; | 
|  | case EFSCTSIZ:	ret = XB;	break; | 
|  | case EFSCTUF:	ret = XB;	break; | 
|  | case EFSCTUI:	ret = XB;	break; | 
|  | case EFSCTUIZ:	ret = XB;	break; | 
|  | case EFSDIV:	ret = AB;	break; | 
|  | case EFSMUL:	ret = AB;	break; | 
|  | case EFSNABS:	ret = XA;	break; | 
|  | case EFSNEG:	ret = XA;	break; | 
|  | case EFSSUB:	ret = AB;	break; | 
|  | case EFSCFSI:	ret = XB;	break; | 
|  |  | 
|  | case EVFSABS:	ret = XA;	break; | 
|  | case EVFSADD:	ret = AB;	break; | 
|  | case EVFSCMPEQ:	ret = XCR;	break; | 
|  | case EVFSCMPGT:	ret = XCR;	break; | 
|  | case EVFSCMPLT:	ret = XCR;	break; | 
|  | case EVFSCTSF:	ret = XB;	break; | 
|  | case EVFSCTSI:	ret = XB;	break; | 
|  | case EVFSCTSIZ:	ret = XB;	break; | 
|  | case EVFSCTUF:	ret = XB;	break; | 
|  | case EVFSCTUI:	ret = XB;	break; | 
|  | case EVFSCTUIZ:	ret = XB;	break; | 
|  | case EVFSDIV:	ret = AB;	break; | 
|  | case EVFSMUL:	ret = AB;	break; | 
|  | case EVFSNABS:	ret = XA;	break; | 
|  | case EVFSNEG:	ret = XA;	break; | 
|  | case EVFSSUB:	ret = AB;	break; | 
|  |  | 
|  | case EFDABS:	ret = XA;	break; | 
|  | case EFDADD:	ret = AB;	break; | 
|  | case EFDCFS:	ret = XB;	break; | 
|  | case EFDCMPEQ:	ret = XCR;	break; | 
|  | case EFDCMPGT:	ret = XCR;	break; | 
|  | case EFDCMPLT:	ret = XCR;	break; | 
|  | case EFDCTSF:	ret = XB;	break; | 
|  | case EFDCTSI:	ret = XB;	break; | 
|  | case EFDCTSIDZ:	ret = XB;	break; | 
|  | case EFDCTSIZ:	ret = XB;	break; | 
|  | case EFDCTUF:	ret = XB;	break; | 
|  | case EFDCTUI:	ret = XB;	break; | 
|  | case EFDCTUIDZ:	ret = XB;	break; | 
|  | case EFDCTUIZ:	ret = XB;	break; | 
|  | case EFDDIV:	ret = AB;	break; | 
|  | case EFDMUL:	ret = AB;	break; | 
|  | case EFDNABS:	ret = XA;	break; | 
|  | case EFDNEG:	ret = XA;	break; | 
|  | case EFDSUB:	ret = AB;	break; | 
|  |  | 
|  | default: | 
|  | printk(KERN_ERR "\nOoops! SPE instruction no type found."); | 
|  | printk(KERN_ERR "\ninst code: %08lx\n", speinsn); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int do_spe_mathemu(struct pt_regs *regs) | 
|  | { | 
|  | FP_DECL_EX; | 
|  | int IR, cmp; | 
|  |  | 
|  | unsigned long type, func, fc, fa, fb, src, speinsn; | 
|  | union dw_union vc, va, vb; | 
|  |  | 
|  | if (get_user(speinsn, (unsigned int __user *) regs->nip)) | 
|  | return -EFAULT; | 
|  | if ((speinsn >> 26) != EFAPU) | 
|  | return -EINVAL;         /* not an spe instruction */ | 
|  |  | 
|  | type = insn_type(speinsn); | 
|  | if (type == NOTYPE) | 
|  | return -ENOSYS; | 
|  |  | 
|  | func = speinsn & 0x7ff; | 
|  | fc = (speinsn >> 21) & 0x1f; | 
|  | fa = (speinsn >> 16) & 0x1f; | 
|  | fb = (speinsn >> 11) & 0x1f; | 
|  | src = (speinsn >> 5) & 0x7; | 
|  |  | 
|  | vc.wp[0] = current->thread.evr[fc]; | 
|  | vc.wp[1] = regs->gpr[fc]; | 
|  | va.wp[0] = current->thread.evr[fa]; | 
|  | va.wp[1] = regs->gpr[fa]; | 
|  | vb.wp[0] = current->thread.evr[fb]; | 
|  | vb.wp[1] = regs->gpr[fb]; | 
|  |  | 
|  | __FPU_FPSCR = mfspr(SPRN_SPEFSCR); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); | 
|  | printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); | 
|  | printk("va: %08x  %08x\n", va.wp[0], va.wp[1]); | 
|  | printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]); | 
|  | #endif | 
|  |  | 
|  | switch (src) { | 
|  | case SPFP: { | 
|  | FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); | 
|  |  | 
|  | switch (type) { | 
|  | case AB: | 
|  | case XCR: | 
|  | FP_UNPACK_SP(SA, va.wp + 1); | 
|  | case XB: | 
|  | FP_UNPACK_SP(SB, vb.wp + 1); | 
|  | break; | 
|  | case XA: | 
|  | FP_UNPACK_SP(SA, va.wp + 1); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); | 
|  | printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); | 
|  | #endif | 
|  |  | 
|  | switch (func) { | 
|  | case EFSABS: | 
|  | vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFSNABS: | 
|  | vc.wp[1] = va.wp[1] | SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFSNEG: | 
|  | vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFSADD: | 
|  | FP_ADD_S(SR, SA, SB); | 
|  | goto pack_s; | 
|  |  | 
|  | case EFSSUB: | 
|  | FP_SUB_S(SR, SA, SB); | 
|  | goto pack_s; | 
|  |  | 
|  | case EFSMUL: | 
|  | FP_MUL_S(SR, SA, SB); | 
|  | goto pack_s; | 
|  |  | 
|  | case EFSDIV: | 
|  | FP_DIV_S(SR, SA, SB); | 
|  | goto pack_s; | 
|  |  | 
|  | case EFSCMPEQ: | 
|  | cmp = 0; | 
|  | goto cmp_s; | 
|  |  | 
|  | case EFSCMPGT: | 
|  | cmp = 1; | 
|  | goto cmp_s; | 
|  |  | 
|  | case EFSCMPLT: | 
|  | cmp = -1; | 
|  | goto cmp_s; | 
|  |  | 
|  | case EFSCTSF: | 
|  | case EFSCTUF: | 
|  | if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) { | 
|  | /* NaN */ | 
|  | if (((vb.wp[1] >> 23) & 0xff) == 0) { | 
|  | /* denorm */ | 
|  | vc.wp[1] = 0x0; | 
|  | } else if ((vb.wp[1] >> 31) == 0) { | 
|  | /* positive normal */ | 
|  | vc.wp[1] = (func == EFSCTSF) ? | 
|  | 0x7fffffff : 0xffffffff; | 
|  | } else { /* negative normal */ | 
|  | vc.wp[1] = (func == EFSCTSF) ? | 
|  | 0x80000000 : 0x0; | 
|  | } | 
|  | } else { /* rB is NaN */ | 
|  | vc.wp[1] = 0x0; | 
|  | } | 
|  | goto update_regs; | 
|  |  | 
|  | case EFSCFD: { | 
|  | FP_DECL_D(DB); | 
|  | FP_CLEAR_EXCEPTIONS; | 
|  | FP_UNPACK_DP(DB, vb.dp); | 
|  | #ifdef DEBUG | 
|  | printk("DB: %ld %08lx %08lx %ld (%ld)\n", | 
|  | DB_s, DB_f1, DB_f0, DB_e, DB_c); | 
|  | #endif | 
|  | FP_CONV(S, D, 1, 2, SR, DB); | 
|  | goto pack_s; | 
|  | } | 
|  |  | 
|  | case EFSCTSI: | 
|  | case EFSCTSIZ: | 
|  | case EFSCTUI: | 
|  | case EFSCTUIZ: | 
|  | if (func & 0x4) { | 
|  | _FP_ROUND(1, SB); | 
|  | } else { | 
|  | _FP_ROUND_ZERO(1, SB); | 
|  | } | 
|  | FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0)); | 
|  | goto update_regs; | 
|  |  | 
|  | default: | 
|  | goto illegal; | 
|  | } | 
|  | break; | 
|  |  | 
|  | pack_s: | 
|  | #ifdef DEBUG | 
|  | printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); | 
|  | #endif | 
|  | FP_PACK_SP(vc.wp + 1, SR); | 
|  | goto update_regs; | 
|  |  | 
|  | cmp_s: | 
|  | FP_CMP_S(IR, SA, SB, 3); | 
|  | if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB))) | 
|  | FP_SET_EXCEPTION(FP_EX_INVALID); | 
|  | if (IR == cmp) { | 
|  | IR = 0x4; | 
|  | } else { | 
|  | IR = 0; | 
|  | } | 
|  | goto update_ccr; | 
|  | } | 
|  |  | 
|  | case DPFP: { | 
|  | FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); | 
|  |  | 
|  | switch (type) { | 
|  | case AB: | 
|  | case XCR: | 
|  | FP_UNPACK_DP(DA, va.dp); | 
|  | case XB: | 
|  | FP_UNPACK_DP(DB, vb.dp); | 
|  | break; | 
|  | case XA: | 
|  | FP_UNPACK_DP(DA, va.dp); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk("DA: %ld %08lx %08lx %ld (%ld)\n", | 
|  | DA_s, DA_f1, DA_f0, DA_e, DA_c); | 
|  | printk("DB: %ld %08lx %08lx %ld (%ld)\n", | 
|  | DB_s, DB_f1, DB_f0, DB_e, DB_c); | 
|  | #endif | 
|  |  | 
|  | switch (func) { | 
|  | case EFDABS: | 
|  | vc.dp[0] = va.dp[0] & ~SIGN_BIT_D; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFDNABS: | 
|  | vc.dp[0] = va.dp[0] | SIGN_BIT_D; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFDNEG: | 
|  | vc.dp[0] = va.dp[0] ^ SIGN_BIT_D; | 
|  | goto update_regs; | 
|  |  | 
|  | case EFDADD: | 
|  | FP_ADD_D(DR, DA, DB); | 
|  | goto pack_d; | 
|  |  | 
|  | case EFDSUB: | 
|  | FP_SUB_D(DR, DA, DB); | 
|  | goto pack_d; | 
|  |  | 
|  | case EFDMUL: | 
|  | FP_MUL_D(DR, DA, DB); | 
|  | goto pack_d; | 
|  |  | 
|  | case EFDDIV: | 
|  | FP_DIV_D(DR, DA, DB); | 
|  | goto pack_d; | 
|  |  | 
|  | case EFDCMPEQ: | 
|  | cmp = 0; | 
|  | goto cmp_d; | 
|  |  | 
|  | case EFDCMPGT: | 
|  | cmp = 1; | 
|  | goto cmp_d; | 
|  |  | 
|  | case EFDCMPLT: | 
|  | cmp = -1; | 
|  | goto cmp_d; | 
|  |  | 
|  | case EFDCTSF: | 
|  | case EFDCTUF: | 
|  | if (!((vb.wp[0] >> 20) == 0x7ff && | 
|  | ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) { | 
|  | /* not a NaN */ | 
|  | if (((vb.wp[0] >> 20) & 0x7ff) == 0) { | 
|  | /* denorm */ | 
|  | vc.wp[1] = 0x0; | 
|  | } else if ((vb.wp[0] >> 31) == 0) { | 
|  | /* positive normal */ | 
|  | vc.wp[1] = (func == EFDCTSF) ? | 
|  | 0x7fffffff : 0xffffffff; | 
|  | } else { /* negative normal */ | 
|  | vc.wp[1] = (func == EFDCTSF) ? | 
|  | 0x80000000 : 0x0; | 
|  | } | 
|  | } else { /* NaN */ | 
|  | vc.wp[1] = 0x0; | 
|  | } | 
|  | goto update_regs; | 
|  |  | 
|  | case EFDCFS: { | 
|  | FP_DECL_S(SB); | 
|  | FP_CLEAR_EXCEPTIONS; | 
|  | FP_UNPACK_SP(SB, vb.wp + 1); | 
|  | #ifdef DEBUG | 
|  | printk("SB: %ld %08lx %ld (%ld)\n", | 
|  | SB_s, SB_f, SB_e, SB_c); | 
|  | #endif | 
|  | FP_CONV(D, S, 2, 1, DR, SB); | 
|  | goto pack_d; | 
|  | } | 
|  |  | 
|  | case EFDCTUIDZ: | 
|  | case EFDCTSIDZ: | 
|  | _FP_ROUND_ZERO(2, DB); | 
|  | FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0)); | 
|  | goto update_regs; | 
|  |  | 
|  | case EFDCTUI: | 
|  | case EFDCTSI: | 
|  | case EFDCTUIZ: | 
|  | case EFDCTSIZ: | 
|  | if (func & 0x4) { | 
|  | _FP_ROUND(2, DB); | 
|  | } else { | 
|  | _FP_ROUND_ZERO(2, DB); | 
|  | } | 
|  | FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0)); | 
|  | goto update_regs; | 
|  |  | 
|  | default: | 
|  | goto illegal; | 
|  | } | 
|  | break; | 
|  |  | 
|  | pack_d: | 
|  | #ifdef DEBUG | 
|  | printk("DR: %ld %08lx %08lx %ld (%ld)\n", | 
|  | DR_s, DR_f1, DR_f0, DR_e, DR_c); | 
|  | #endif | 
|  | FP_PACK_DP(vc.dp, DR); | 
|  | goto update_regs; | 
|  |  | 
|  | cmp_d: | 
|  | FP_CMP_D(IR, DA, DB, 3); | 
|  | if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB))) | 
|  | FP_SET_EXCEPTION(FP_EX_INVALID); | 
|  | if (IR == cmp) { | 
|  | IR = 0x4; | 
|  | } else { | 
|  | IR = 0; | 
|  | } | 
|  | goto update_ccr; | 
|  |  | 
|  | } | 
|  |  | 
|  | case VCT: { | 
|  | FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0); | 
|  | FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1); | 
|  | int IR0, IR1; | 
|  |  | 
|  | switch (type) { | 
|  | case AB: | 
|  | case XCR: | 
|  | FP_UNPACK_SP(SA0, va.wp); | 
|  | FP_UNPACK_SP(SA1, va.wp + 1); | 
|  | case XB: | 
|  | FP_UNPACK_SP(SB0, vb.wp); | 
|  | FP_UNPACK_SP(SB1, vb.wp + 1); | 
|  | break; | 
|  | case XA: | 
|  | FP_UNPACK_SP(SA0, va.wp); | 
|  | FP_UNPACK_SP(SA1, va.wp + 1); | 
|  | break; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c); | 
|  | printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c); | 
|  | printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c); | 
|  | printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c); | 
|  | #endif | 
|  |  | 
|  | switch (func) { | 
|  | case EVFSABS: | 
|  | vc.wp[0] = va.wp[0] & ~SIGN_BIT_S; | 
|  | vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EVFSNABS: | 
|  | vc.wp[0] = va.wp[0] | SIGN_BIT_S; | 
|  | vc.wp[1] = va.wp[1] | SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EVFSNEG: | 
|  | vc.wp[0] = va.wp[0] ^ SIGN_BIT_S; | 
|  | vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; | 
|  | goto update_regs; | 
|  |  | 
|  | case EVFSADD: | 
|  | FP_ADD_S(SR0, SA0, SB0); | 
|  | FP_ADD_S(SR1, SA1, SB1); | 
|  | goto pack_vs; | 
|  |  | 
|  | case EVFSSUB: | 
|  | FP_SUB_S(SR0, SA0, SB0); | 
|  | FP_SUB_S(SR1, SA1, SB1); | 
|  | goto pack_vs; | 
|  |  | 
|  | case EVFSMUL: | 
|  | FP_MUL_S(SR0, SA0, SB0); | 
|  | FP_MUL_S(SR1, SA1, SB1); | 
|  | goto pack_vs; | 
|  |  | 
|  | case EVFSDIV: | 
|  | FP_DIV_S(SR0, SA0, SB0); | 
|  | FP_DIV_S(SR1, SA1, SB1); | 
|  | goto pack_vs; | 
|  |  | 
|  | case EVFSCMPEQ: | 
|  | cmp = 0; | 
|  | goto cmp_vs; | 
|  |  | 
|  | case EVFSCMPGT: | 
|  | cmp = 1; | 
|  | goto cmp_vs; | 
|  |  | 
|  | case EVFSCMPLT: | 
|  | cmp = -1; | 
|  | goto cmp_vs; | 
|  |  | 
|  | case EVFSCTSF: | 
|  | __asm__ __volatile__ ("mtspr 512, %4\n" | 
|  | "efsctsf %0, %2\n" | 
|  | "efsctsf %1, %3\n" | 
|  | : "=r" (vc.wp[0]), "=r" (vc.wp[1]) | 
|  | : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); | 
|  | goto update_regs; | 
|  |  | 
|  | case EVFSCTUF: | 
|  | __asm__ __volatile__ ("mtspr 512, %4\n" | 
|  | "efsctuf %0, %2\n" | 
|  | "efsctuf %1, %3\n" | 
|  | : "=r" (vc.wp[0]), "=r" (vc.wp[1]) | 
|  | : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); | 
|  | goto update_regs; | 
|  |  | 
|  | case EVFSCTUI: | 
|  | case EVFSCTSI: | 
|  | case EVFSCTUIZ: | 
|  | case EVFSCTSIZ: | 
|  | if (func & 0x4) { | 
|  | _FP_ROUND(1, SB0); | 
|  | _FP_ROUND(1, SB1); | 
|  | } else { | 
|  | _FP_ROUND_ZERO(1, SB0); | 
|  | _FP_ROUND_ZERO(1, SB1); | 
|  | } | 
|  | FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0)); | 
|  | FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0)); | 
|  | goto update_regs; | 
|  |  | 
|  | default: | 
|  | goto illegal; | 
|  | } | 
|  | break; | 
|  |  | 
|  | pack_vs: | 
|  | #ifdef DEBUG | 
|  | printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c); | 
|  | printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c); | 
|  | #endif | 
|  | FP_PACK_SP(vc.wp, SR0); | 
|  | FP_PACK_SP(vc.wp + 1, SR1); | 
|  | goto update_regs; | 
|  |  | 
|  | cmp_vs: | 
|  | { | 
|  | int ch, cl; | 
|  |  | 
|  | FP_CMP_S(IR0, SA0, SB0, 3); | 
|  | FP_CMP_S(IR1, SA1, SB1, 3); | 
|  | if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0))) | 
|  | FP_SET_EXCEPTION(FP_EX_INVALID); | 
|  | if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1))) | 
|  | FP_SET_EXCEPTION(FP_EX_INVALID); | 
|  | ch = (IR0 == cmp) ? 1 : 0; | 
|  | cl = (IR1 == cmp) ? 1 : 0; | 
|  | IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) | | 
|  | ((ch & cl) << 0); | 
|  | goto update_ccr; | 
|  | } | 
|  | } | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | update_ccr: | 
|  | regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2)); | 
|  | regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2)); | 
|  |  | 
|  | update_regs: | 
|  | __FPU_FPSCR &= ~FP_EX_MASK; | 
|  | __FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK); | 
|  | mtspr(SPRN_SPEFSCR, __FPU_FPSCR); | 
|  |  | 
|  | current->thread.evr[fc] = vc.wp[0]; | 
|  | regs->gpr[fc] = vc.wp[1]; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk("ccr = %08lx\n", regs->ccr); | 
|  | printk("cur exceptions = %08x spefscr = %08lx\n", | 
|  | FP_CUR_EXCEPTIONS, __FPU_FPSCR); | 
|  | printk("vc: %08x  %08x\n", vc.wp[0], vc.wp[1]); | 
|  | printk("va: %08x  %08x\n", va.wp[0], va.wp[1]); | 
|  | printk("vb: %08x  %08x\n", vb.wp[0], vb.wp[1]); | 
|  | #endif | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | illegal: | 
|  | printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn); | 
|  | return -ENOSYS; | 
|  | } | 
|  |  | 
|  | int speround_handler(struct pt_regs *regs) | 
|  | { | 
|  | union dw_union fgpr; | 
|  | int s_lo, s_hi; | 
|  | unsigned long speinsn, type, fc; | 
|  |  | 
|  | if (get_user(speinsn, (unsigned int __user *) regs->nip)) | 
|  | return -EFAULT; | 
|  | if ((speinsn >> 26) != 4) | 
|  | return -EINVAL;         /* not an spe instruction */ | 
|  |  | 
|  | type = insn_type(speinsn & 0x7ff); | 
|  | if (type == XCR) return -ENOSYS; | 
|  |  | 
|  | fc = (speinsn >> 21) & 0x1f; | 
|  | s_lo = regs->gpr[fc] & SIGN_BIT_S; | 
|  | s_hi = current->thread.evr[fc] & SIGN_BIT_S; | 
|  | fgpr.wp[0] = current->thread.evr[fc]; | 
|  | fgpr.wp[1] = regs->gpr[fc]; | 
|  |  | 
|  | __FPU_FPSCR = mfspr(SPRN_SPEFSCR); | 
|  |  | 
|  | switch ((speinsn >> 5) & 0x7) { | 
|  | /* Since SPE instructions on E500 core can handle round to nearest | 
|  | * and round toward zero with IEEE-754 complied, we just need | 
|  | * to handle round toward +Inf and round toward -Inf by software. | 
|  | */ | 
|  | case SPFP: | 
|  | if ((FP_ROUNDMODE) == FP_RND_PINF) { | 
|  | if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */ | 
|  | } else { /* round to -Inf */ | 
|  | if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */ | 
|  | } | 
|  | break; | 
|  |  | 
|  | case DPFP: | 
|  | if (FP_ROUNDMODE == FP_RND_PINF) { | 
|  | if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */ | 
|  | } else { /* round to -Inf */ | 
|  | if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */ | 
|  | } | 
|  | break; | 
|  |  | 
|  | case VCT: | 
|  | if (FP_ROUNDMODE == FP_RND_PINF) { | 
|  | if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ | 
|  | if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */ | 
|  | } else { /* round to -Inf */ | 
|  | if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ | 
|  | if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | current->thread.evr[fc] = fgpr.wp[0]; | 
|  | regs->gpr[fc] = fgpr.wp[1]; | 
|  |  | 
|  | return 0; | 
|  | } |