1*6a800f36SLiu Yu /* 2*6a800f36SLiu Yu * arch/powerpc/math-emu/math_efp.c 3*6a800f36SLiu Yu * 4*6a800f36SLiu Yu * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. 5*6a800f36SLiu Yu * 6*6a800f36SLiu Yu * Author: Ebony Zhu, <ebony.zhu@freescale.com> 7*6a800f36SLiu Yu * Yu Liu, <yu.liu@freescale.com> 8*6a800f36SLiu Yu * 9*6a800f36SLiu Yu * Derived from arch/alpha/math-emu/math.c 10*6a800f36SLiu Yu * arch/powerpc/math-emu/math.c 11*6a800f36SLiu Yu * 12*6a800f36SLiu Yu * Description: 13*6a800f36SLiu Yu * This file is the exception handler to make E500 SPE instructions 14*6a800f36SLiu Yu * fully comply with IEEE-754 floating point standard. 15*6a800f36SLiu Yu * 16*6a800f36SLiu Yu * This program is free software; you can redistribute it and/or 17*6a800f36SLiu Yu * modify it under the terms of the GNU General Public License 18*6a800f36SLiu Yu * as published by the Free Software Foundation; either version 19*6a800f36SLiu Yu * 2 of the License, or (at your option) any later version. 20*6a800f36SLiu Yu */ 21*6a800f36SLiu Yu 22*6a800f36SLiu Yu #include <linux/types.h> 23*6a800f36SLiu Yu 24*6a800f36SLiu Yu #include <asm/uaccess.h> 25*6a800f36SLiu Yu #include <asm/reg.h> 26*6a800f36SLiu Yu 27*6a800f36SLiu Yu #define FP_EX_BOOKE_E500_SPE 28*6a800f36SLiu Yu #include <asm/sfp-machine.h> 29*6a800f36SLiu Yu 30*6a800f36SLiu Yu #include <math-emu/soft-fp.h> 31*6a800f36SLiu Yu #include <math-emu/single.h> 32*6a800f36SLiu Yu #include <math-emu/double.h> 33*6a800f36SLiu Yu 34*6a800f36SLiu Yu #define EFAPU 0x4 35*6a800f36SLiu Yu 36*6a800f36SLiu Yu #define VCT 0x4 37*6a800f36SLiu Yu #define SPFP 0x6 38*6a800f36SLiu Yu #define DPFP 0x7 39*6a800f36SLiu Yu 40*6a800f36SLiu Yu #define EFSADD 0x2c0 41*6a800f36SLiu Yu #define EFSSUB 0x2c1 42*6a800f36SLiu Yu #define EFSABS 0x2c4 43*6a800f36SLiu Yu #define EFSNABS 0x2c5 44*6a800f36SLiu Yu #define EFSNEG 0x2c6 45*6a800f36SLiu Yu #define EFSMUL 0x2c8 46*6a800f36SLiu Yu #define EFSDIV 0x2c9 47*6a800f36SLiu Yu #define EFSCMPGT 0x2cc 48*6a800f36SLiu Yu #define EFSCMPLT 0x2cd 49*6a800f36SLiu Yu #define EFSCMPEQ 0x2ce 50*6a800f36SLiu Yu #define EFSCFD 0x2cf 51*6a800f36SLiu Yu #define EFSCFSI 0x2d1 52*6a800f36SLiu Yu #define EFSCTUI 0x2d4 53*6a800f36SLiu Yu #define EFSCTSI 0x2d5 54*6a800f36SLiu Yu #define EFSCTUF 0x2d6 55*6a800f36SLiu Yu #define EFSCTSF 0x2d7 56*6a800f36SLiu Yu #define EFSCTUIZ 0x2d8 57*6a800f36SLiu Yu #define EFSCTSIZ 0x2da 58*6a800f36SLiu Yu 59*6a800f36SLiu Yu #define EVFSADD 0x280 60*6a800f36SLiu Yu #define EVFSSUB 0x281 61*6a800f36SLiu Yu #define EVFSABS 0x284 62*6a800f36SLiu Yu #define EVFSNABS 0x285 63*6a800f36SLiu Yu #define EVFSNEG 0x286 64*6a800f36SLiu Yu #define EVFSMUL 0x288 65*6a800f36SLiu Yu #define EVFSDIV 0x289 66*6a800f36SLiu Yu #define EVFSCMPGT 0x28c 67*6a800f36SLiu Yu #define EVFSCMPLT 0x28d 68*6a800f36SLiu Yu #define EVFSCMPEQ 0x28e 69*6a800f36SLiu Yu #define EVFSCTUI 0x294 70*6a800f36SLiu Yu #define EVFSCTSI 0x295 71*6a800f36SLiu Yu #define EVFSCTUF 0x296 72*6a800f36SLiu Yu #define EVFSCTSF 0x297 73*6a800f36SLiu Yu #define EVFSCTUIZ 0x298 74*6a800f36SLiu Yu #define EVFSCTSIZ 0x29a 75*6a800f36SLiu Yu 76*6a800f36SLiu Yu #define EFDADD 0x2e0 77*6a800f36SLiu Yu #define EFDSUB 0x2e1 78*6a800f36SLiu Yu #define EFDABS 0x2e4 79*6a800f36SLiu Yu #define EFDNABS 0x2e5 80*6a800f36SLiu Yu #define EFDNEG 0x2e6 81*6a800f36SLiu Yu #define EFDMUL 0x2e8 82*6a800f36SLiu Yu #define EFDDIV 0x2e9 83*6a800f36SLiu Yu #define EFDCTUIDZ 0x2ea 84*6a800f36SLiu Yu #define EFDCTSIDZ 0x2eb 85*6a800f36SLiu Yu #define EFDCMPGT 0x2ec 86*6a800f36SLiu Yu #define EFDCMPLT 0x2ed 87*6a800f36SLiu Yu #define EFDCMPEQ 0x2ee 88*6a800f36SLiu Yu #define EFDCFS 0x2ef 89*6a800f36SLiu Yu #define EFDCTUI 0x2f4 90*6a800f36SLiu Yu #define EFDCTSI 0x2f5 91*6a800f36SLiu Yu #define EFDCTUF 0x2f6 92*6a800f36SLiu Yu #define EFDCTSF 0x2f7 93*6a800f36SLiu Yu #define EFDCTUIZ 0x2f8 94*6a800f36SLiu Yu #define EFDCTSIZ 0x2fa 95*6a800f36SLiu Yu 96*6a800f36SLiu Yu #define AB 2 97*6a800f36SLiu Yu #define XA 3 98*6a800f36SLiu Yu #define XB 4 99*6a800f36SLiu Yu #define XCR 5 100*6a800f36SLiu Yu #define NOTYPE 0 101*6a800f36SLiu Yu 102*6a800f36SLiu Yu #define SIGN_BIT_S (1UL << 31) 103*6a800f36SLiu Yu #define SIGN_BIT_D (1ULL << 63) 104*6a800f36SLiu Yu #define FP_EX_MASK (FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \ 105*6a800f36SLiu Yu FP_EX_UNDERFLOW | FP_EX_OVERFLOW) 106*6a800f36SLiu Yu 107*6a800f36SLiu Yu union dw_union { 108*6a800f36SLiu Yu u64 dp[1]; 109*6a800f36SLiu Yu u32 wp[2]; 110*6a800f36SLiu Yu }; 111*6a800f36SLiu Yu 112*6a800f36SLiu Yu static unsigned long insn_type(unsigned long speinsn) 113*6a800f36SLiu Yu { 114*6a800f36SLiu Yu unsigned long ret = NOTYPE; 115*6a800f36SLiu Yu 116*6a800f36SLiu Yu switch (speinsn & 0x7ff) { 117*6a800f36SLiu Yu case EFSABS: ret = XA; break; 118*6a800f36SLiu Yu case EFSADD: ret = AB; break; 119*6a800f36SLiu Yu case EFSCFD: ret = XB; break; 120*6a800f36SLiu Yu case EFSCMPEQ: ret = XCR; break; 121*6a800f36SLiu Yu case EFSCMPGT: ret = XCR; break; 122*6a800f36SLiu Yu case EFSCMPLT: ret = XCR; break; 123*6a800f36SLiu Yu case EFSCTSF: ret = XB; break; 124*6a800f36SLiu Yu case EFSCTSI: ret = XB; break; 125*6a800f36SLiu Yu case EFSCTSIZ: ret = XB; break; 126*6a800f36SLiu Yu case EFSCTUF: ret = XB; break; 127*6a800f36SLiu Yu case EFSCTUI: ret = XB; break; 128*6a800f36SLiu Yu case EFSCTUIZ: ret = XB; break; 129*6a800f36SLiu Yu case EFSDIV: ret = AB; break; 130*6a800f36SLiu Yu case EFSMUL: ret = AB; break; 131*6a800f36SLiu Yu case EFSNABS: ret = XA; break; 132*6a800f36SLiu Yu case EFSNEG: ret = XA; break; 133*6a800f36SLiu Yu case EFSSUB: ret = AB; break; 134*6a800f36SLiu Yu case EFSCFSI: ret = XB; break; 135*6a800f36SLiu Yu 136*6a800f36SLiu Yu case EVFSABS: ret = XA; break; 137*6a800f36SLiu Yu case EVFSADD: ret = AB; break; 138*6a800f36SLiu Yu case EVFSCMPEQ: ret = XCR; break; 139*6a800f36SLiu Yu case EVFSCMPGT: ret = XCR; break; 140*6a800f36SLiu Yu case EVFSCMPLT: ret = XCR; break; 141*6a800f36SLiu Yu case EVFSCTSF: ret = XB; break; 142*6a800f36SLiu Yu case EVFSCTSI: ret = XB; break; 143*6a800f36SLiu Yu case EVFSCTSIZ: ret = XB; break; 144*6a800f36SLiu Yu case EVFSCTUF: ret = XB; break; 145*6a800f36SLiu Yu case EVFSCTUI: ret = XB; break; 146*6a800f36SLiu Yu case EVFSCTUIZ: ret = XB; break; 147*6a800f36SLiu Yu case EVFSDIV: ret = AB; break; 148*6a800f36SLiu Yu case EVFSMUL: ret = AB; break; 149*6a800f36SLiu Yu case EVFSNABS: ret = XA; break; 150*6a800f36SLiu Yu case EVFSNEG: ret = XA; break; 151*6a800f36SLiu Yu case EVFSSUB: ret = AB; break; 152*6a800f36SLiu Yu 153*6a800f36SLiu Yu case EFDABS: ret = XA; break; 154*6a800f36SLiu Yu case EFDADD: ret = AB; break; 155*6a800f36SLiu Yu case EFDCFS: ret = XB; break; 156*6a800f36SLiu Yu case EFDCMPEQ: ret = XCR; break; 157*6a800f36SLiu Yu case EFDCMPGT: ret = XCR; break; 158*6a800f36SLiu Yu case EFDCMPLT: ret = XCR; break; 159*6a800f36SLiu Yu case EFDCTSF: ret = XB; break; 160*6a800f36SLiu Yu case EFDCTSI: ret = XB; break; 161*6a800f36SLiu Yu case EFDCTSIDZ: ret = XB; break; 162*6a800f36SLiu Yu case EFDCTSIZ: ret = XB; break; 163*6a800f36SLiu Yu case EFDCTUF: ret = XB; break; 164*6a800f36SLiu Yu case EFDCTUI: ret = XB; break; 165*6a800f36SLiu Yu case EFDCTUIDZ: ret = XB; break; 166*6a800f36SLiu Yu case EFDCTUIZ: ret = XB; break; 167*6a800f36SLiu Yu case EFDDIV: ret = AB; break; 168*6a800f36SLiu Yu case EFDMUL: ret = AB; break; 169*6a800f36SLiu Yu case EFDNABS: ret = XA; break; 170*6a800f36SLiu Yu case EFDNEG: ret = XA; break; 171*6a800f36SLiu Yu case EFDSUB: ret = AB; break; 172*6a800f36SLiu Yu 173*6a800f36SLiu Yu default: 174*6a800f36SLiu Yu printk(KERN_ERR "\nOoops! SPE instruction no type found."); 175*6a800f36SLiu Yu printk(KERN_ERR "\ninst code: %08lx\n", speinsn); 176*6a800f36SLiu Yu } 177*6a800f36SLiu Yu 178*6a800f36SLiu Yu return ret; 179*6a800f36SLiu Yu } 180*6a800f36SLiu Yu 181*6a800f36SLiu Yu int do_spe_mathemu(struct pt_regs *regs) 182*6a800f36SLiu Yu { 183*6a800f36SLiu Yu FP_DECL_EX; 184*6a800f36SLiu Yu int IR, cmp; 185*6a800f36SLiu Yu 186*6a800f36SLiu Yu unsigned long type, func, fc, fa, fb, src, speinsn; 187*6a800f36SLiu Yu union dw_union vc, va, vb; 188*6a800f36SLiu Yu 189*6a800f36SLiu Yu if (get_user(speinsn, (unsigned int __user *) regs->nip)) 190*6a800f36SLiu Yu return -EFAULT; 191*6a800f36SLiu Yu if ((speinsn >> 26) != EFAPU) 192*6a800f36SLiu Yu return -EINVAL; /* not an spe instruction */ 193*6a800f36SLiu Yu 194*6a800f36SLiu Yu type = insn_type(speinsn); 195*6a800f36SLiu Yu if (type == NOTYPE) 196*6a800f36SLiu Yu return -ENOSYS; 197*6a800f36SLiu Yu 198*6a800f36SLiu Yu func = speinsn & 0x7ff; 199*6a800f36SLiu Yu fc = (speinsn >> 21) & 0x1f; 200*6a800f36SLiu Yu fa = (speinsn >> 16) & 0x1f; 201*6a800f36SLiu Yu fb = (speinsn >> 11) & 0x1f; 202*6a800f36SLiu Yu src = (speinsn >> 5) & 0x7; 203*6a800f36SLiu Yu 204*6a800f36SLiu Yu vc.wp[0] = current->thread.evr[fc]; 205*6a800f36SLiu Yu vc.wp[1] = regs->gpr[fc]; 206*6a800f36SLiu Yu va.wp[0] = current->thread.evr[fa]; 207*6a800f36SLiu Yu va.wp[1] = regs->gpr[fa]; 208*6a800f36SLiu Yu vb.wp[0] = current->thread.evr[fb]; 209*6a800f36SLiu Yu vb.wp[1] = regs->gpr[fb]; 210*6a800f36SLiu Yu 211*6a800f36SLiu Yu __FPU_FPSCR = mfspr(SPRN_SPEFSCR); 212*6a800f36SLiu Yu 213*6a800f36SLiu Yu #ifdef DEBUG 214*6a800f36SLiu Yu printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR); 215*6a800f36SLiu Yu printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); 216*6a800f36SLiu Yu printk("va: %08x %08x\n", va.wp[0], va.wp[1]); 217*6a800f36SLiu Yu printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); 218*6a800f36SLiu Yu #endif 219*6a800f36SLiu Yu 220*6a800f36SLiu Yu switch (src) { 221*6a800f36SLiu Yu case SPFP: { 222*6a800f36SLiu Yu FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); 223*6a800f36SLiu Yu 224*6a800f36SLiu Yu switch (type) { 225*6a800f36SLiu Yu case AB: 226*6a800f36SLiu Yu case XCR: 227*6a800f36SLiu Yu FP_UNPACK_SP(SA, va.wp + 1); 228*6a800f36SLiu Yu case XB: 229*6a800f36SLiu Yu FP_UNPACK_SP(SB, vb.wp + 1); 230*6a800f36SLiu Yu break; 231*6a800f36SLiu Yu case XA: 232*6a800f36SLiu Yu FP_UNPACK_SP(SA, va.wp + 1); 233*6a800f36SLiu Yu break; 234*6a800f36SLiu Yu } 235*6a800f36SLiu Yu 236*6a800f36SLiu Yu #ifdef DEBUG 237*6a800f36SLiu Yu printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c); 238*6a800f36SLiu Yu printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c); 239*6a800f36SLiu Yu #endif 240*6a800f36SLiu Yu 241*6a800f36SLiu Yu switch (func) { 242*6a800f36SLiu Yu case EFSABS: 243*6a800f36SLiu Yu vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; 244*6a800f36SLiu Yu goto update_regs; 245*6a800f36SLiu Yu 246*6a800f36SLiu Yu case EFSNABS: 247*6a800f36SLiu Yu vc.wp[1] = va.wp[1] | SIGN_BIT_S; 248*6a800f36SLiu Yu goto update_regs; 249*6a800f36SLiu Yu 250*6a800f36SLiu Yu case EFSNEG: 251*6a800f36SLiu Yu vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; 252*6a800f36SLiu Yu goto update_regs; 253*6a800f36SLiu Yu 254*6a800f36SLiu Yu case EFSADD: 255*6a800f36SLiu Yu FP_ADD_S(SR, SA, SB); 256*6a800f36SLiu Yu goto pack_s; 257*6a800f36SLiu Yu 258*6a800f36SLiu Yu case EFSSUB: 259*6a800f36SLiu Yu FP_SUB_S(SR, SA, SB); 260*6a800f36SLiu Yu goto pack_s; 261*6a800f36SLiu Yu 262*6a800f36SLiu Yu case EFSMUL: 263*6a800f36SLiu Yu FP_MUL_S(SR, SA, SB); 264*6a800f36SLiu Yu goto pack_s; 265*6a800f36SLiu Yu 266*6a800f36SLiu Yu case EFSDIV: 267*6a800f36SLiu Yu FP_DIV_S(SR, SA, SB); 268*6a800f36SLiu Yu goto pack_s; 269*6a800f36SLiu Yu 270*6a800f36SLiu Yu case EFSCMPEQ: 271*6a800f36SLiu Yu cmp = 0; 272*6a800f36SLiu Yu goto cmp_s; 273*6a800f36SLiu Yu 274*6a800f36SLiu Yu case EFSCMPGT: 275*6a800f36SLiu Yu cmp = 1; 276*6a800f36SLiu Yu goto cmp_s; 277*6a800f36SLiu Yu 278*6a800f36SLiu Yu case EFSCMPLT: 279*6a800f36SLiu Yu cmp = -1; 280*6a800f36SLiu Yu goto cmp_s; 281*6a800f36SLiu Yu 282*6a800f36SLiu Yu case EFSCTSF: 283*6a800f36SLiu Yu case EFSCTUF: 284*6a800f36SLiu Yu if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) { 285*6a800f36SLiu Yu /* NaN */ 286*6a800f36SLiu Yu if (((vb.wp[1] >> 23) & 0xff) == 0) { 287*6a800f36SLiu Yu /* denorm */ 288*6a800f36SLiu Yu vc.wp[1] = 0x0; 289*6a800f36SLiu Yu } else if ((vb.wp[1] >> 31) == 0) { 290*6a800f36SLiu Yu /* positive normal */ 291*6a800f36SLiu Yu vc.wp[1] = (func == EFSCTSF) ? 292*6a800f36SLiu Yu 0x7fffffff : 0xffffffff; 293*6a800f36SLiu Yu } else { /* negative normal */ 294*6a800f36SLiu Yu vc.wp[1] = (func == EFSCTSF) ? 295*6a800f36SLiu Yu 0x80000000 : 0x0; 296*6a800f36SLiu Yu } 297*6a800f36SLiu Yu } else { /* rB is NaN */ 298*6a800f36SLiu Yu vc.wp[1] = 0x0; 299*6a800f36SLiu Yu } 300*6a800f36SLiu Yu goto update_regs; 301*6a800f36SLiu Yu 302*6a800f36SLiu Yu case EFSCFD: { 303*6a800f36SLiu Yu FP_DECL_D(DB); 304*6a800f36SLiu Yu FP_CLEAR_EXCEPTIONS; 305*6a800f36SLiu Yu FP_UNPACK_DP(DB, vb.dp); 306*6a800f36SLiu Yu #ifdef DEBUG 307*6a800f36SLiu Yu printk("DB: %ld %08lx %08lx %ld (%ld)\n", 308*6a800f36SLiu Yu DB_s, DB_f1, DB_f0, DB_e, DB_c); 309*6a800f36SLiu Yu #endif 310*6a800f36SLiu Yu FP_CONV(S, D, 1, 2, SR, DB); 311*6a800f36SLiu Yu goto pack_s; 312*6a800f36SLiu Yu } 313*6a800f36SLiu Yu 314*6a800f36SLiu Yu case EFSCTSI: 315*6a800f36SLiu Yu case EFSCTSIZ: 316*6a800f36SLiu Yu case EFSCTUI: 317*6a800f36SLiu Yu case EFSCTUIZ: 318*6a800f36SLiu Yu if (func & 0x4) { 319*6a800f36SLiu Yu _FP_ROUND(1, SB); 320*6a800f36SLiu Yu } else { 321*6a800f36SLiu Yu _FP_ROUND_ZERO(1, SB); 322*6a800f36SLiu Yu } 323*6a800f36SLiu Yu FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0)); 324*6a800f36SLiu Yu goto update_regs; 325*6a800f36SLiu Yu 326*6a800f36SLiu Yu default: 327*6a800f36SLiu Yu goto illegal; 328*6a800f36SLiu Yu } 329*6a800f36SLiu Yu break; 330*6a800f36SLiu Yu 331*6a800f36SLiu Yu pack_s: 332*6a800f36SLiu Yu #ifdef DEBUG 333*6a800f36SLiu Yu printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c); 334*6a800f36SLiu Yu #endif 335*6a800f36SLiu Yu FP_PACK_SP(vc.wp + 1, SR); 336*6a800f36SLiu Yu goto update_regs; 337*6a800f36SLiu Yu 338*6a800f36SLiu Yu cmp_s: 339*6a800f36SLiu Yu FP_CMP_S(IR, SA, SB, 3); 340*6a800f36SLiu Yu if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB))) 341*6a800f36SLiu Yu FP_SET_EXCEPTION(FP_EX_INVALID); 342*6a800f36SLiu Yu if (IR == cmp) { 343*6a800f36SLiu Yu IR = 0x4; 344*6a800f36SLiu Yu } else { 345*6a800f36SLiu Yu IR = 0; 346*6a800f36SLiu Yu } 347*6a800f36SLiu Yu goto update_ccr; 348*6a800f36SLiu Yu } 349*6a800f36SLiu Yu 350*6a800f36SLiu Yu case DPFP: { 351*6a800f36SLiu Yu FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR); 352*6a800f36SLiu Yu 353*6a800f36SLiu Yu switch (type) { 354*6a800f36SLiu Yu case AB: 355*6a800f36SLiu Yu case XCR: 356*6a800f36SLiu Yu FP_UNPACK_DP(DA, va.dp); 357*6a800f36SLiu Yu case XB: 358*6a800f36SLiu Yu FP_UNPACK_DP(DB, vb.dp); 359*6a800f36SLiu Yu break; 360*6a800f36SLiu Yu case XA: 361*6a800f36SLiu Yu FP_UNPACK_DP(DA, va.dp); 362*6a800f36SLiu Yu break; 363*6a800f36SLiu Yu } 364*6a800f36SLiu Yu 365*6a800f36SLiu Yu #ifdef DEBUG 366*6a800f36SLiu Yu printk("DA: %ld %08lx %08lx %ld (%ld)\n", 367*6a800f36SLiu Yu DA_s, DA_f1, DA_f0, DA_e, DA_c); 368*6a800f36SLiu Yu printk("DB: %ld %08lx %08lx %ld (%ld)\n", 369*6a800f36SLiu Yu DB_s, DB_f1, DB_f0, DB_e, DB_c); 370*6a800f36SLiu Yu #endif 371*6a800f36SLiu Yu 372*6a800f36SLiu Yu switch (func) { 373*6a800f36SLiu Yu case EFDABS: 374*6a800f36SLiu Yu vc.dp[0] = va.dp[0] & ~SIGN_BIT_D; 375*6a800f36SLiu Yu goto update_regs; 376*6a800f36SLiu Yu 377*6a800f36SLiu Yu case EFDNABS: 378*6a800f36SLiu Yu vc.dp[0] = va.dp[0] | SIGN_BIT_D; 379*6a800f36SLiu Yu goto update_regs; 380*6a800f36SLiu Yu 381*6a800f36SLiu Yu case EFDNEG: 382*6a800f36SLiu Yu vc.dp[0] = va.dp[0] ^ SIGN_BIT_D; 383*6a800f36SLiu Yu goto update_regs; 384*6a800f36SLiu Yu 385*6a800f36SLiu Yu case EFDADD: 386*6a800f36SLiu Yu FP_ADD_D(DR, DA, DB); 387*6a800f36SLiu Yu goto pack_d; 388*6a800f36SLiu Yu 389*6a800f36SLiu Yu case EFDSUB: 390*6a800f36SLiu Yu FP_SUB_D(DR, DA, DB); 391*6a800f36SLiu Yu goto pack_d; 392*6a800f36SLiu Yu 393*6a800f36SLiu Yu case EFDMUL: 394*6a800f36SLiu Yu FP_MUL_D(DR, DA, DB); 395*6a800f36SLiu Yu goto pack_d; 396*6a800f36SLiu Yu 397*6a800f36SLiu Yu case EFDDIV: 398*6a800f36SLiu Yu FP_DIV_D(DR, DA, DB); 399*6a800f36SLiu Yu goto pack_d; 400*6a800f36SLiu Yu 401*6a800f36SLiu Yu case EFDCMPEQ: 402*6a800f36SLiu Yu cmp = 0; 403*6a800f36SLiu Yu goto cmp_d; 404*6a800f36SLiu Yu 405*6a800f36SLiu Yu case EFDCMPGT: 406*6a800f36SLiu Yu cmp = 1; 407*6a800f36SLiu Yu goto cmp_d; 408*6a800f36SLiu Yu 409*6a800f36SLiu Yu case EFDCMPLT: 410*6a800f36SLiu Yu cmp = -1; 411*6a800f36SLiu Yu goto cmp_d; 412*6a800f36SLiu Yu 413*6a800f36SLiu Yu case EFDCTSF: 414*6a800f36SLiu Yu case EFDCTUF: 415*6a800f36SLiu Yu if (!((vb.wp[0] >> 20) == 0x7ff && 416*6a800f36SLiu Yu ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) { 417*6a800f36SLiu Yu /* not a NaN */ 418*6a800f36SLiu Yu if (((vb.wp[0] >> 20) & 0x7ff) == 0) { 419*6a800f36SLiu Yu /* denorm */ 420*6a800f36SLiu Yu vc.wp[1] = 0x0; 421*6a800f36SLiu Yu } else if ((vb.wp[0] >> 31) == 0) { 422*6a800f36SLiu Yu /* positive normal */ 423*6a800f36SLiu Yu vc.wp[1] = (func == EFDCTSF) ? 424*6a800f36SLiu Yu 0x7fffffff : 0xffffffff; 425*6a800f36SLiu Yu } else { /* negative normal */ 426*6a800f36SLiu Yu vc.wp[1] = (func == EFDCTSF) ? 427*6a800f36SLiu Yu 0x80000000 : 0x0; 428*6a800f36SLiu Yu } 429*6a800f36SLiu Yu } else { /* NaN */ 430*6a800f36SLiu Yu vc.wp[1] = 0x0; 431*6a800f36SLiu Yu } 432*6a800f36SLiu Yu goto update_regs; 433*6a800f36SLiu Yu 434*6a800f36SLiu Yu case EFDCFS: { 435*6a800f36SLiu Yu FP_DECL_S(SB); 436*6a800f36SLiu Yu FP_CLEAR_EXCEPTIONS; 437*6a800f36SLiu Yu FP_UNPACK_SP(SB, vb.wp + 1); 438*6a800f36SLiu Yu #ifdef DEBUG 439*6a800f36SLiu Yu printk("SB: %ld %08lx %ld (%ld)\n", 440*6a800f36SLiu Yu SB_s, SB_f, SB_e, SB_c); 441*6a800f36SLiu Yu #endif 442*6a800f36SLiu Yu FP_CONV(D, S, 2, 1, DR, SB); 443*6a800f36SLiu Yu goto pack_d; 444*6a800f36SLiu Yu } 445*6a800f36SLiu Yu 446*6a800f36SLiu Yu case EFDCTUIDZ: 447*6a800f36SLiu Yu case EFDCTSIDZ: 448*6a800f36SLiu Yu _FP_ROUND_ZERO(2, DB); 449*6a800f36SLiu Yu FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0)); 450*6a800f36SLiu Yu goto update_regs; 451*6a800f36SLiu Yu 452*6a800f36SLiu Yu case EFDCTUI: 453*6a800f36SLiu Yu case EFDCTSI: 454*6a800f36SLiu Yu case EFDCTUIZ: 455*6a800f36SLiu Yu case EFDCTSIZ: 456*6a800f36SLiu Yu if (func & 0x4) { 457*6a800f36SLiu Yu _FP_ROUND(2, DB); 458*6a800f36SLiu Yu } else { 459*6a800f36SLiu Yu _FP_ROUND_ZERO(2, DB); 460*6a800f36SLiu Yu } 461*6a800f36SLiu Yu FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0)); 462*6a800f36SLiu Yu goto update_regs; 463*6a800f36SLiu Yu 464*6a800f36SLiu Yu default: 465*6a800f36SLiu Yu goto illegal; 466*6a800f36SLiu Yu } 467*6a800f36SLiu Yu break; 468*6a800f36SLiu Yu 469*6a800f36SLiu Yu pack_d: 470*6a800f36SLiu Yu #ifdef DEBUG 471*6a800f36SLiu Yu printk("DR: %ld %08lx %08lx %ld (%ld)\n", 472*6a800f36SLiu Yu DR_s, DR_f1, DR_f0, DR_e, DR_c); 473*6a800f36SLiu Yu #endif 474*6a800f36SLiu Yu FP_PACK_DP(vc.dp, DR); 475*6a800f36SLiu Yu goto update_regs; 476*6a800f36SLiu Yu 477*6a800f36SLiu Yu cmp_d: 478*6a800f36SLiu Yu FP_CMP_D(IR, DA, DB, 3); 479*6a800f36SLiu Yu if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB))) 480*6a800f36SLiu Yu FP_SET_EXCEPTION(FP_EX_INVALID); 481*6a800f36SLiu Yu if (IR == cmp) { 482*6a800f36SLiu Yu IR = 0x4; 483*6a800f36SLiu Yu } else { 484*6a800f36SLiu Yu IR = 0; 485*6a800f36SLiu Yu } 486*6a800f36SLiu Yu goto update_ccr; 487*6a800f36SLiu Yu 488*6a800f36SLiu Yu } 489*6a800f36SLiu Yu 490*6a800f36SLiu Yu case VCT: { 491*6a800f36SLiu Yu FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0); 492*6a800f36SLiu Yu FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1); 493*6a800f36SLiu Yu int IR0, IR1; 494*6a800f36SLiu Yu 495*6a800f36SLiu Yu switch (type) { 496*6a800f36SLiu Yu case AB: 497*6a800f36SLiu Yu case XCR: 498*6a800f36SLiu Yu FP_UNPACK_SP(SA0, va.wp); 499*6a800f36SLiu Yu FP_UNPACK_SP(SA1, va.wp + 1); 500*6a800f36SLiu Yu case XB: 501*6a800f36SLiu Yu FP_UNPACK_SP(SB0, vb.wp); 502*6a800f36SLiu Yu FP_UNPACK_SP(SB1, vb.wp + 1); 503*6a800f36SLiu Yu break; 504*6a800f36SLiu Yu case XA: 505*6a800f36SLiu Yu FP_UNPACK_SP(SA0, va.wp); 506*6a800f36SLiu Yu FP_UNPACK_SP(SA1, va.wp + 1); 507*6a800f36SLiu Yu break; 508*6a800f36SLiu Yu } 509*6a800f36SLiu Yu 510*6a800f36SLiu Yu #ifdef DEBUG 511*6a800f36SLiu Yu printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c); 512*6a800f36SLiu Yu printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c); 513*6a800f36SLiu Yu printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c); 514*6a800f36SLiu Yu printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c); 515*6a800f36SLiu Yu #endif 516*6a800f36SLiu Yu 517*6a800f36SLiu Yu switch (func) { 518*6a800f36SLiu Yu case EVFSABS: 519*6a800f36SLiu Yu vc.wp[0] = va.wp[0] & ~SIGN_BIT_S; 520*6a800f36SLiu Yu vc.wp[1] = va.wp[1] & ~SIGN_BIT_S; 521*6a800f36SLiu Yu goto update_regs; 522*6a800f36SLiu Yu 523*6a800f36SLiu Yu case EVFSNABS: 524*6a800f36SLiu Yu vc.wp[0] = va.wp[0] | SIGN_BIT_S; 525*6a800f36SLiu Yu vc.wp[1] = va.wp[1] | SIGN_BIT_S; 526*6a800f36SLiu Yu goto update_regs; 527*6a800f36SLiu Yu 528*6a800f36SLiu Yu case EVFSNEG: 529*6a800f36SLiu Yu vc.wp[0] = va.wp[0] ^ SIGN_BIT_S; 530*6a800f36SLiu Yu vc.wp[1] = va.wp[1] ^ SIGN_BIT_S; 531*6a800f36SLiu Yu goto update_regs; 532*6a800f36SLiu Yu 533*6a800f36SLiu Yu case EVFSADD: 534*6a800f36SLiu Yu FP_ADD_S(SR0, SA0, SB0); 535*6a800f36SLiu Yu FP_ADD_S(SR1, SA1, SB1); 536*6a800f36SLiu Yu goto pack_vs; 537*6a800f36SLiu Yu 538*6a800f36SLiu Yu case EVFSSUB: 539*6a800f36SLiu Yu FP_SUB_S(SR0, SA0, SB0); 540*6a800f36SLiu Yu FP_SUB_S(SR1, SA1, SB1); 541*6a800f36SLiu Yu goto pack_vs; 542*6a800f36SLiu Yu 543*6a800f36SLiu Yu case EVFSMUL: 544*6a800f36SLiu Yu FP_MUL_S(SR0, SA0, SB0); 545*6a800f36SLiu Yu FP_MUL_S(SR1, SA1, SB1); 546*6a800f36SLiu Yu goto pack_vs; 547*6a800f36SLiu Yu 548*6a800f36SLiu Yu case EVFSDIV: 549*6a800f36SLiu Yu FP_DIV_S(SR0, SA0, SB0); 550*6a800f36SLiu Yu FP_DIV_S(SR1, SA1, SB1); 551*6a800f36SLiu Yu goto pack_vs; 552*6a800f36SLiu Yu 553*6a800f36SLiu Yu case EVFSCMPEQ: 554*6a800f36SLiu Yu cmp = 0; 555*6a800f36SLiu Yu goto cmp_vs; 556*6a800f36SLiu Yu 557*6a800f36SLiu Yu case EVFSCMPGT: 558*6a800f36SLiu Yu cmp = 1; 559*6a800f36SLiu Yu goto cmp_vs; 560*6a800f36SLiu Yu 561*6a800f36SLiu Yu case EVFSCMPLT: 562*6a800f36SLiu Yu cmp = -1; 563*6a800f36SLiu Yu goto cmp_vs; 564*6a800f36SLiu Yu 565*6a800f36SLiu Yu case EVFSCTSF: 566*6a800f36SLiu Yu __asm__ __volatile__ ("mtspr 512, %4\n" 567*6a800f36SLiu Yu "efsctsf %0, %2\n" 568*6a800f36SLiu Yu "efsctsf %1, %3\n" 569*6a800f36SLiu Yu : "=r" (vc.wp[0]), "=r" (vc.wp[1]) 570*6a800f36SLiu Yu : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); 571*6a800f36SLiu Yu goto update_regs; 572*6a800f36SLiu Yu 573*6a800f36SLiu Yu case EVFSCTUF: 574*6a800f36SLiu Yu __asm__ __volatile__ ("mtspr 512, %4\n" 575*6a800f36SLiu Yu "efsctuf %0, %2\n" 576*6a800f36SLiu Yu "efsctuf %1, %3\n" 577*6a800f36SLiu Yu : "=r" (vc.wp[0]), "=r" (vc.wp[1]) 578*6a800f36SLiu Yu : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0)); 579*6a800f36SLiu Yu goto update_regs; 580*6a800f36SLiu Yu 581*6a800f36SLiu Yu case EVFSCTUI: 582*6a800f36SLiu Yu case EVFSCTSI: 583*6a800f36SLiu Yu case EVFSCTUIZ: 584*6a800f36SLiu Yu case EVFSCTSIZ: 585*6a800f36SLiu Yu if (func & 0x4) { 586*6a800f36SLiu Yu _FP_ROUND(1, SB0); 587*6a800f36SLiu Yu _FP_ROUND(1, SB1); 588*6a800f36SLiu Yu } else { 589*6a800f36SLiu Yu _FP_ROUND_ZERO(1, SB0); 590*6a800f36SLiu Yu _FP_ROUND_ZERO(1, SB1); 591*6a800f36SLiu Yu } 592*6a800f36SLiu Yu FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0)); 593*6a800f36SLiu Yu FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0)); 594*6a800f36SLiu Yu goto update_regs; 595*6a800f36SLiu Yu 596*6a800f36SLiu Yu default: 597*6a800f36SLiu Yu goto illegal; 598*6a800f36SLiu Yu } 599*6a800f36SLiu Yu break; 600*6a800f36SLiu Yu 601*6a800f36SLiu Yu pack_vs: 602*6a800f36SLiu Yu #ifdef DEBUG 603*6a800f36SLiu Yu printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c); 604*6a800f36SLiu Yu printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c); 605*6a800f36SLiu Yu #endif 606*6a800f36SLiu Yu FP_PACK_SP(vc.wp, SR0); 607*6a800f36SLiu Yu FP_PACK_SP(vc.wp + 1, SR1); 608*6a800f36SLiu Yu goto update_regs; 609*6a800f36SLiu Yu 610*6a800f36SLiu Yu cmp_vs: 611*6a800f36SLiu Yu { 612*6a800f36SLiu Yu int ch, cl; 613*6a800f36SLiu Yu 614*6a800f36SLiu Yu FP_CMP_S(IR0, SA0, SB0, 3); 615*6a800f36SLiu Yu FP_CMP_S(IR1, SA1, SB1, 3); 616*6a800f36SLiu Yu if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0))) 617*6a800f36SLiu Yu FP_SET_EXCEPTION(FP_EX_INVALID); 618*6a800f36SLiu Yu if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1))) 619*6a800f36SLiu Yu FP_SET_EXCEPTION(FP_EX_INVALID); 620*6a800f36SLiu Yu ch = (IR0 == cmp) ? 1 : 0; 621*6a800f36SLiu Yu cl = (IR1 == cmp) ? 1 : 0; 622*6a800f36SLiu Yu IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) | 623*6a800f36SLiu Yu ((ch & cl) << 0); 624*6a800f36SLiu Yu goto update_ccr; 625*6a800f36SLiu Yu } 626*6a800f36SLiu Yu } 627*6a800f36SLiu Yu default: 628*6a800f36SLiu Yu return -EINVAL; 629*6a800f36SLiu Yu } 630*6a800f36SLiu Yu 631*6a800f36SLiu Yu update_ccr: 632*6a800f36SLiu Yu regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2)); 633*6a800f36SLiu Yu regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2)); 634*6a800f36SLiu Yu 635*6a800f36SLiu Yu update_regs: 636*6a800f36SLiu Yu __FPU_FPSCR &= ~FP_EX_MASK; 637*6a800f36SLiu Yu __FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK); 638*6a800f36SLiu Yu mtspr(SPRN_SPEFSCR, __FPU_FPSCR); 639*6a800f36SLiu Yu 640*6a800f36SLiu Yu current->thread.evr[fc] = vc.wp[0]; 641*6a800f36SLiu Yu regs->gpr[fc] = vc.wp[1]; 642*6a800f36SLiu Yu 643*6a800f36SLiu Yu #ifdef DEBUG 644*6a800f36SLiu Yu printk("ccr = %08lx\n", regs->ccr); 645*6a800f36SLiu Yu printk("cur exceptions = %08x spefscr = %08lx\n", 646*6a800f36SLiu Yu FP_CUR_EXCEPTIONS, __FPU_FPSCR); 647*6a800f36SLiu Yu printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]); 648*6a800f36SLiu Yu printk("va: %08x %08x\n", va.wp[0], va.wp[1]); 649*6a800f36SLiu Yu printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]); 650*6a800f36SLiu Yu #endif 651*6a800f36SLiu Yu 652*6a800f36SLiu Yu return 0; 653*6a800f36SLiu Yu 654*6a800f36SLiu Yu illegal: 655*6a800f36SLiu Yu printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn); 656*6a800f36SLiu Yu return -ENOSYS; 657*6a800f36SLiu Yu } 658*6a800f36SLiu Yu 659*6a800f36SLiu Yu int speround_handler(struct pt_regs *regs) 660*6a800f36SLiu Yu { 661*6a800f36SLiu Yu union dw_union fgpr; 662*6a800f36SLiu Yu int s_lo, s_hi; 663*6a800f36SLiu Yu unsigned long speinsn, type, fc; 664*6a800f36SLiu Yu 665*6a800f36SLiu Yu if (get_user(speinsn, (unsigned int __user *) regs->nip)) 666*6a800f36SLiu Yu return -EFAULT; 667*6a800f36SLiu Yu if ((speinsn >> 26) != 4) 668*6a800f36SLiu Yu return -EINVAL; /* not an spe instruction */ 669*6a800f36SLiu Yu 670*6a800f36SLiu Yu type = insn_type(speinsn & 0x7ff); 671*6a800f36SLiu Yu if (type == XCR) return -ENOSYS; 672*6a800f36SLiu Yu 673*6a800f36SLiu Yu fc = (speinsn >> 21) & 0x1f; 674*6a800f36SLiu Yu s_lo = regs->gpr[fc] & SIGN_BIT_S; 675*6a800f36SLiu Yu s_hi = current->thread.evr[fc] & SIGN_BIT_S; 676*6a800f36SLiu Yu fgpr.wp[0] = current->thread.evr[fc]; 677*6a800f36SLiu Yu fgpr.wp[1] = regs->gpr[fc]; 678*6a800f36SLiu Yu 679*6a800f36SLiu Yu __FPU_FPSCR = mfspr(SPRN_SPEFSCR); 680*6a800f36SLiu Yu 681*6a800f36SLiu Yu switch ((speinsn >> 5) & 0x7) { 682*6a800f36SLiu Yu /* Since SPE instructions on E500 core can handle round to nearest 683*6a800f36SLiu Yu * and round toward zero with IEEE-754 complied, we just need 684*6a800f36SLiu Yu * to handle round toward +Inf and round toward -Inf by software. 685*6a800f36SLiu Yu */ 686*6a800f36SLiu Yu case SPFP: 687*6a800f36SLiu Yu if ((FP_ROUNDMODE) == FP_RND_PINF) { 688*6a800f36SLiu Yu if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */ 689*6a800f36SLiu Yu } else { /* round to -Inf */ 690*6a800f36SLiu Yu if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */ 691*6a800f36SLiu Yu } 692*6a800f36SLiu Yu break; 693*6a800f36SLiu Yu 694*6a800f36SLiu Yu case DPFP: 695*6a800f36SLiu Yu if (FP_ROUNDMODE == FP_RND_PINF) { 696*6a800f36SLiu Yu if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */ 697*6a800f36SLiu Yu } else { /* round to -Inf */ 698*6a800f36SLiu Yu if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */ 699*6a800f36SLiu Yu } 700*6a800f36SLiu Yu break; 701*6a800f36SLiu Yu 702*6a800f36SLiu Yu case VCT: 703*6a800f36SLiu Yu if (FP_ROUNDMODE == FP_RND_PINF) { 704*6a800f36SLiu Yu if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */ 705*6a800f36SLiu Yu if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */ 706*6a800f36SLiu Yu } else { /* round to -Inf */ 707*6a800f36SLiu Yu if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */ 708*6a800f36SLiu Yu if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */ 709*6a800f36SLiu Yu } 710*6a800f36SLiu Yu break; 711*6a800f36SLiu Yu 712*6a800f36SLiu Yu default: 713*6a800f36SLiu Yu return -EINVAL; 714*6a800f36SLiu Yu } 715*6a800f36SLiu Yu 716*6a800f36SLiu Yu current->thread.evr[fc] = fgpr.wp[0]; 717*6a800f36SLiu Yu regs->gpr[fc] = fgpr.wp[1]; 718*6a800f36SLiu Yu 719*6a800f36SLiu Yu return 0; 720*6a800f36SLiu Yu } 721