721 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			721 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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;
 | |
| }
 |