1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2005-2018 Andes Technology Corporation
4 #include <asm/bitfield.h>
5 #include <asm/uaccess.h>
6 #include <asm/sfp-machine.h>
7 #include <asm/fpuemu.h>
8 #include <asm/nds32_fpu_inst.h>
10 #define DPFROMREG(dp, x) (dp = (void *)((unsigned long *)fpu_reg + 2*x))
12 #define SPFROMREG(sp, x)\
13 ((sp) = (void *)((unsigned long *)fpu_reg + (x^1)))
15 #define SPFROMREG(sp, x) ((sp) = (void *)((unsigned long *)fpu_reg + x))
18 #define DEF3OP(name, p, f1, f2) \
19 void fpemu_##name##p(void *ft, void *fa, void *fb) \
25 #define DEF3OPNEG(name, p, f1, f2, f3) \
26 void fpemu_##name##p(void *ft, void *fa, void *fb) \
32 DEF3OP(fmadd, s, fmuls, fadds);
33 DEF3OP(fmsub, s, fmuls, fsubs);
34 DEF3OP(fmadd, d, fmuld, faddd);
35 DEF3OP(fmsub, d, fmuld, fsubd);
36 DEF3OPNEG(fnmadd, s, fmuls, fadds, fnegs);
37 DEF3OPNEG(fnmsub, s, fmuls, fsubs, fnegs);
38 DEF3OPNEG(fnmadd, d, fmuld, faddd, fnegd);
39 DEF3OPNEG(fnmsub, d, fmuld, fsubd, fnegd);
41 static const unsigned char cmptab[8] = {
63 void (*t)(void *ft, void *fa, void *fb);
64 void (*b)(void *ft, void *fa);
67 * Emulate a single FPU arithmetic instruction.
69 static int fpu_emu(struct fpu_struct *fpu_reg, unsigned long insn)
71 int rfmt; /* resulting format */
75 switch (rfmt = NDS32Insn_OPCODE_COP0(insn)) {
77 switch (NDS32Insn_OPCODE_BIT69(insn)) {
87 func.t = fpemu_fmadds;
91 func.t = fpemu_fmsubs;
95 func.t = fpemu_fnmadds;
99 func.t = fpemu_fnmsubs;
111 switch (NDS32Insn_OPCODE_BIT1014(insn)) {
154 switch (NDS32Insn_OPCODE_BIT69(insn)) {
170 switch (NDS32Insn_OPCODE_BIT69(insn)) {
180 func.t = fpemu_fmaddd;
184 func.t = fpemu_fmsubd;
188 func.t = fpemu_fnmaddd;
192 func.t = fpemu_fnmsubd;
204 switch (NDS32Insn_OPCODE_BIT1014(insn)) {
249 switch (NDS32Insn_OPCODE_BIT69(insn)) {
273 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
274 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
281 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
282 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
283 SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
290 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
291 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
296 unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn);
299 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
300 SPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
301 SPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
303 cmpop = cmptab[cmpop];
304 fcmps(ft, fa, fb, cmpop);
312 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
313 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
320 DPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
321 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
322 DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
329 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
330 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
335 unsigned int cmpop = NDS32Insn_OPCODE_BIT69(insn);
338 SPFROMREG(ft, NDS32Insn_OPCODE_Rt(insn));
339 DPFROMREG(fa, NDS32Insn_OPCODE_Ra(insn));
340 DPFROMREG(fb, NDS32Insn_OPCODE_Rb(insn));
342 cmpop = cmptab[cmpop];
343 fcmpd(ft, fa, fb, cmpop);
353 * If an exception is required, generate a tidy SIGFPE exception.
355 #if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
356 if (((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE_NO_UDF_IEXE)
357 || ((fpu_reg->fpcsr << 5) & (fpu_reg->UDF_IEX_trap))) {
359 if ((fpu_reg->fpcsr << 5) & fpu_reg->fpcsr & FPCSR_mskALLE) {
366 int do_fpuemu(struct pt_regs *regs, struct fpu_struct *fpu)
368 unsigned long insn = 0, addr = regs->ipc;
369 unsigned long emulpc, contpc;
370 unsigned char *pc = (void *)&insn;
374 for (i = 0; i < 4; i++) {
375 if (__get_user(c, (unsigned char *)addr++))
380 insn = be32_to_cpu(insn);
383 contpc = regs->ipc + 4;
385 if (NDS32Insn_OPCODE(insn) != cop0_op)
388 switch (NDS32Insn_OPCODE_COP0(insn)) {
394 /* a real fpu computation instruction */
395 ret = fpu_emu(fpu, insn);