MIPS: math-emu: Add support for the MIPS R6 SELEQZ FPU instruction
[linux-drm-fsl-dcu.git] / arch / mips / math-emu / cp1emu.c
index 8a5b0eb4ddef441448bab23a1603afdf466fd39f..02ba536f159486e5cd6e78ba2aba29e1308d7cd8 100644 (file)
@@ -1394,6 +1394,14 @@ static const unsigned char cmptab[8] = {
        IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN,        /* cmp_ule (sig) cmp_ngt */
 };
 
+static const unsigned char negative_cmptab[8] = {
+       0, /* Reserved */
+       IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ,
+       IEEE754_CLT | IEEE754_CGT | IEEE754_CUN,
+       IEEE754_CLT | IEEE754_CGT,
+       /* Reserved */
+};
+
 
 /*
  * Additional MIPS4 instructions
@@ -1735,6 +1743,17 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
                        SPFROMREG(rv.s, MIPSInst_FS(ir));
                        break;
 
+               case fseleqz_op:
+                       if (!cpu_has_mips_r6)
+                               return SIGILL;
+
+                       SPFROMREG(rv.s, MIPSInst_FT(ir));
+                       if (rv.w & 0x1)
+                               rv.w = 0;
+                       else
+                               SPFROMREG(rv.s, MIPSInst_FS(ir));
+                       break;
+
                case fabs_op:
                        handler.u = ieee754sp_abs;
                        goto scopuop;
@@ -1838,7 +1857,7 @@ copcsr:
                        goto copcsr;
 
                default:
-                       if (MIPSInst_FUNC(ir) >= fcmp_op) {
+                       if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
                                unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
                                union ieee754sp fs, ft;
 
@@ -1932,6 +1951,18 @@ copcsr:
                                return 0;
                        DPFROMREG(rv.d, MIPSInst_FS(ir));
                        break;
+
+               case fseleqz_op:
+                       if (!cpu_has_mips_r6)
+                               return SIGILL;
+
+                       DPFROMREG(rv.d, MIPSInst_FT(ir));
+                       if (rv.l & 0x1)
+                               rv.l = 0;
+                       else
+                               DPFROMREG(rv.d, MIPSInst_FS(ir));
+                       break;
+
                case fabs_op:
                        handler.u = ieee754dp_abs;
                        goto dcopuop;
@@ -2015,7 +2046,7 @@ dcopuop:
                        goto copcsr;
 
                default:
-                       if (MIPSInst_FUNC(ir) >= fcmp_op) {
+                       if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
                                unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
                                union ieee754dp fs, ft;
 
@@ -2057,10 +2088,65 @@ dcopuop:
                        rv.d = ieee754dp_fint(fs.bits);
                        rfmt = d_fmt;
                        goto copcsr;
-               default:
-                       return SIGILL;
+               default: {
+                       /* Emulating the new CMP.condn.fmt R6 instruction */
+#define CMPOP_MASK     0x7
+#define SIGN_BIT       (0x1 << 3)
+#define PREDICATE_BIT  (0x1 << 4)
+
+                       int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK;
+                       int sig = MIPSInst_FUNC(ir) & SIGN_BIT;
+                       union ieee754sp fs, ft;
+
+                       /* This is an R6 only instruction */
+                       if (!cpu_has_mips_r6 ||
+                           (MIPSInst_FUNC(ir) & 0x20))
+                               return SIGILL;
+
+                       /* fmt is w_fmt for single precision so fix it */
+                       rfmt = s_fmt;
+                       /* default to false */
+                       rv.w = 0;
+
+                       /* CMP.condn.S */
+                       SPFROMREG(fs, MIPSInst_FS(ir));
+                       SPFROMREG(ft, MIPSInst_FT(ir));
+
+                       /* positive predicates */
+                       if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
+                               if (ieee754sp_cmp(fs, ft, cmptab[cmpop],
+                                                 sig))
+                                   rv.w = -1; /* true, all 1s */
+                               if ((sig) &&
+                                   ieee754_cxtest(IEEE754_INVALID_OPERATION))
+                                       rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
+                               else
+                                       goto copcsr;
+                       } else {
+                               /* negative predicates */
+                               switch (cmpop) {
+                               case 1:
+                               case 2:
+                               case 3:
+                                       if (ieee754sp_cmp(fs, ft,
+                                                         negative_cmptab[cmpop],
+                                                         sig))
+                                               rv.w = -1; /* true, all 1s */
+                                       if (sig &&
+                                           ieee754_cxtest(IEEE754_INVALID_OPERATION))
+                                               rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
+                                       else
+                                               goto copcsr;
+                                       break;
+                               default:
+                                       /* Reserved R6 ops */
+                                       pr_err("Reserved MIPS R6 CMP.condn.S operation\n");
+                                       return SIGILL;
+                               }
+                       }
+                       break;
+                       }
                }
-               break;
        }
 
        case l_fmt:
@@ -2081,11 +2167,60 @@ dcopuop:
                        rv.d = ieee754dp_flong(bits);
                        rfmt = d_fmt;
                        goto copcsr;
-               default:
-                       return SIGILL;
-               }
-               break;
+               default: {
+                       /* Emulating the new CMP.condn.fmt R6 instruction */
+                       int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK;
+                       int sig = MIPSInst_FUNC(ir) & SIGN_BIT;
+                       union ieee754dp fs, ft;
+
+                       if (!cpu_has_mips_r6 ||
+                           (MIPSInst_FUNC(ir) & 0x20))
+                               return SIGILL;
 
+                       /* fmt is l_fmt for double precision so fix it */
+                       rfmt = d_fmt;
+                       /* default to false */
+                       rv.l = 0;
+
+                       /* CMP.condn.D */
+                       DPFROMREG(fs, MIPSInst_FS(ir));
+                       DPFROMREG(ft, MIPSInst_FT(ir));
+
+                       /* positive predicates */
+                       if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
+                               if (ieee754dp_cmp(fs, ft,
+                                                 cmptab[cmpop], sig))
+                                   rv.l = -1LL; /* true, all 1s */
+                               if (sig &&
+                                   ieee754_cxtest(IEEE754_INVALID_OPERATION))
+                                       rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
+                               else
+                                       goto copcsr;
+                       } else {
+                               /* negative predicates */
+                               switch (cmpop) {
+                               case 1:
+                               case 2:
+                               case 3:
+                                       if (ieee754dp_cmp(fs, ft,
+                                                         negative_cmptab[cmpop],
+                                                         sig))
+                                               rv.l = -1LL; /* true, all 1s */
+                                       if (sig &&
+                                           ieee754_cxtest(IEEE754_INVALID_OPERATION))
+                                               rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
+                                       else
+                                               goto copcsr;
+                                       break;
+                               default:
+                                       /* Reserved R6 ops */
+                                       pr_err("Reserved MIPS R6 CMP.condn.D operation\n");
+                                       return SIGILL;
+                               }
+                       }
+                       break;
+                       }
+               }
        default:
                return SIGILL;
        }