MIPS: math-emu: Add support for the CMP.condn.fmt R6 instruction
[linux-drm-fsl-dcu.git] / arch / mips / math-emu / cp1emu.c
index 8a5b0eb..ef41fc8 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
@@ -1838,7 +1846,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;
 
@@ -2015,7 +2023,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 +2065,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 +2144,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;
        }