MIPS: math-emu: Add support for the MIPS R6 SELEQZ FPU instruction
[linux-drm-fsl-dcu.git] / arch / mips / math-emu / cp1emu.c
index 712f17a2ecf206e1b5d33a63956e62baac29cb08..02ba536f159486e5cd6e78ba2aba29e1308d7cd8 100644 (file)
@@ -1137,7 +1137,7 @@ emul:
                        break;
 
                case mfhc_op:
-                       if (!cpu_has_mips_r2)
+                       if (!cpu_has_mips_r2_r6)
                                goto sigill;
 
                        /* copregister rd -> gpr[rt] */
@@ -1148,7 +1148,7 @@ emul:
                        break;
 
                case mthc_op:
-                       if (!cpu_has_mips_r2)
+                       if (!cpu_has_mips_r2_r6)
                                goto sigill;
 
                        /* copregister rd <- gpr[rt] */
@@ -1181,6 +1181,24 @@ emul:
                        }
                        break;
 
+               case bc1eqz_op:
+               case bc1nez_op:
+                       if (!cpu_has_mips_r6 || delay_slot(xcp))
+                               return SIGILL;
+
+                       cond = likely = 0;
+                       switch (MIPSInst_RS(ir)) {
+                       case bc1eqz_op:
+                               if (get_fpr32(&current->thread.fpu.fpr[MIPSInst_RT(ir)], 0) & 0x1)
+                                   cond = 1;
+                               break;
+                       case bc1nez_op:
+                               if (!(get_fpr32(&current->thread.fpu.fpr[MIPSInst_RT(ir)], 0) & 0x1))
+                                   cond = 1;
+                               break;
+                       }
+                       goto branch_common;
+
                case bc_op:
                        if (delay_slot(xcp))
                                return SIGILL;
@@ -1207,7 +1225,7 @@ emul:
                        case bct_op:
                                break;
                        }
-
+branch_common:
                        set_delay_slot(xcp);
                        if (cond) {
                                /*
@@ -1376,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
@@ -1717,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;
@@ -1820,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;
 
@@ -1914,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;
@@ -1997,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;
 
@@ -2021,8 +2070,11 @@ dcopuop:
                        break;
                }
                break;
+       }
+
+       case w_fmt: {
+               union ieee754dp fs;
 
-       case w_fmt:
                switch (MIPSInst_FUNC(ir)) {
                case fcvts_op:
                        /* convert word to single precision real */
@@ -2036,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:
@@ -2060,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;
        }