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;
 | 
						|
}
 |