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
);