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