else                                                            \
                set_thread_flag(TIF_32BIT_FPREGS);                      \
                                                                        \
+       clear_thread_flag(TIF_HYBRID_FPREGS);                           \
+                                                                       \
        if (personality(current->personality) != PER_LINUX)             \
                set_personality(PER_LINUX);                             \
                                                                        \
                                                                        \
        clear_thread_flag(TIF_32BIT_REGS);                              \
        clear_thread_flag(TIF_32BIT_FPREGS);                            \
+       clear_thread_flag(TIF_HYBRID_FPREGS);                           \
        clear_thread_flag(TIF_32BIT_ADDR);                              \
                                                                        \
        if ((ex).e_ident[EI_CLASS] == ELFCLASS32)                       \
 
 
 /*
  * This enum specifies a mode in which we want the FPU to operate, for cores
- * which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
- * purposefully have the values 0 & 1 respectively, so that an integer value
- * of Status.FR can be trivially casted to the corresponding enum fpu_mode.
+ * which implement the Status.FR bit. Note that the bottom bit of the value
+ * purposefully matches the desired value of the Status.FR bit.
  */
 enum fpu_mode {
        FPU_32BIT = 0,          /* FR = 0 */
-       FPU_64BIT,              /* FR = 1 */
+       FPU_64BIT,              /* FR = 1, FRE = 0 */
        FPU_AS_IS,
+       FPU_HYBRID,             /* FR = 1, FRE = 1 */
+
+#define FPU_FR_MASK            0x1
 };
 
 static inline int __enable_fpu(enum fpu_mode mode)
                enable_fpu_hazard();
                return 0;
 
+       case FPU_HYBRID:
+               if (!cpu_has_fre)
+                       return SIGFPE;
+
+               /* set FRE */
+               write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
+               goto fr_common;
+
        case FPU_64BIT:
 #if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
                /* we only have a 32-bit FPU */
 #endif
                /* fall through */
        case FPU_32BIT:
+               /* clear FRE */
+               write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
+fr_common:
                /* set CU1 & change FR appropriately */
-               fr = (int)mode;
+               fr = (int)mode & FPU_FR_MASK;
                change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
                enable_fpu_hazard();
 
        enum fpu_mode mode;
        int ret;
 
-       mode = !test_thread_flag(TIF_32BIT_FPREGS);
+       if (test_thread_flag(TIF_HYBRID_FPREGS))
+               mode = FPU_HYBRID;
+       else
+               mode = !test_thread_flag(TIF_32BIT_FPREGS);
+
        ret = __enable_fpu(mode);
        if (ret)
                return ret;
 
        KSTK_STATUS(current) |= ST0_CU1;
-       if (mode == FPU_64BIT)
+       if (mode == FPU_64BIT || mode == FPU_HYBRID)
                KSTK_STATUS(current) |= ST0_FR;
        else /* mode == FPU_32BIT */
                KSTK_STATUS(current) &= ~ST0_FR;
 
        if (cpu_has_fpu) {
                ret = __own_fpu();
-               if (!ret)
+               if (!ret) {
+                       unsigned int config5 = read_c0_config5();
+
+                       /*
+                        * Ensure FRE is clear whilst running _init_fpu, since
+                        * single precision FP instructions are used. If FRE
+                        * was set then we'll just end up initialising all 32
+                        * 64b registers.
+                        */
+                       write_c0_config5(config5 & ~MIPS_CONF5_FRE);
+                       enable_fpu_hazard();
+
                        _init_fpu();
+
+                       /* Restore FRE */
+                       write_c0_config5(config5);
+                       enable_fpu_hazard();
+               }
        } else
                fpu_emulator_init_fpu();
 
 
 #define TIF_LOAD_WATCH         25      /* If set, load watch registers */
 #define TIF_SYSCALL_TRACEPOINT 26      /* syscall tracepoint instrumentation */
 #define TIF_32BIT_FPREGS       27      /* 32-bit floating point registers */
+#define TIF_HYBRID_FPREGS      28      /* 64b FP registers, odd singles in bits 63:32 of even doubles */
 #define TIF_USEDMSA            29      /* MSA has been used this quantum */
 #define TIF_MSA_CTX_LIVE       30      /* MSA context must be preserved */
 #define TIF_SYSCALL_TRACE      31      /* syscall trace active */
 #define _TIF_FPUBOUND          (1<<TIF_FPUBOUND)
 #define _TIF_LOAD_WATCH                (1<<TIF_LOAD_WATCH)
 #define _TIF_32BIT_FPREGS      (1<<TIF_32BIT_FPREGS)
+#define _TIF_HYBRID_FPREGS     (1<<TIF_HYBRID_FPREGS)
 #define _TIF_USEDMSA           (1<<TIF_USEDMSA)
 #define _TIF_MSA_CTX_LIVE      (1<<TIF_MSA_CTX_LIVE)
 #define _TIF_SYSCALL_TRACEPOINT        (1<<TIF_SYSCALL_TRACEPOINT)
 
        }
 }
 
+static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
+                      unsigned long old_epc, unsigned long old_ra)
+{
+       union mips_instruction inst = { .word = opcode };
+       void __user *fault_addr = NULL;
+       int sig;
+
+       /* If it's obviously not an FP instruction, skip it */
+       switch (inst.i_format.opcode) {
+       case cop1_op:
+       case cop1x_op:
+       case lwc1_op:
+       case ldc1_op:
+       case swc1_op:
+       case sdc1_op:
+               break;
+
+       default:
+               return -1;
+       }
+
+       /*
+        * do_ri skipped over the instruction via compute_return_epc, undo
+        * that for the FPU emulator.
+        */
+       regs->cp0_epc = old_epc;
+       regs->regs[31] = old_ra;
+
+       /* Save the FP context to struct thread_struct */
+       lose_fpu(1);
+
+       /* Run the emulator */
+       sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1,
+                                      &fault_addr);
+
+       /* If something went wrong, signal */
+       process_fpemu_return(sig, fault_addr);
+
+       /* Restore the hardware register state */
+       own_fpu(1);
+
+       return 0;
+}
+
 /*
  * XXX Delayed fp exceptions when doing a lazy ctx switch XXX
  */
 
                if (status < 0)
                        status = simulate_sync(regs, opcode);
+
+               if (status < 0)
+                       status = simulate_fp(regs, opcode, old_epc, old31);
        }
 
        if (status < 0)
 
        return !test_thread_flag(TIF_32BIT_FPREGS);
 }
 
+static inline bool hybrid_fprs(void)
+{
+       return test_thread_flag(TIF_HYBRID_FPREGS);
+}
+
 #define SIFROMREG(si, x)                                               \
 do {                                                                   \
-       if (cop1_64bit(xcp))                                            \
+       if (cop1_64bit(xcp) && !hybrid_fprs())                          \
                (si) = (int)get_fpr32(&ctx->fpr[x], 0);                 \
        else                                                            \
                (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1);    \
 
 #define SITOREG(si, x)                                                 \
 do {                                                                   \
-       if (cop1_64bit(xcp)) {                                          \
+       if (cop1_64bit(xcp) && !hybrid_fprs()) {                        \
                unsigned i;                                             \
                set_fpr32(&ctx->fpr[x], 0, si);                         \
                for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++)     \