18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/* Copyright (C) 2005-2018 Andes Technology Corporation */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#ifndef __ASM_NDS32_FPU_H
58c2ecf20Sopenharmony_ci#define __ASM_NDS32_FPU_H
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_FPU)
88c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__
98c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h>
108c2ecf20Sopenharmony_ci#include <linux/preempt.h>
118c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ciextern bool has_fpu;
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciextern void save_fpu(struct task_struct *__tsk);
168c2ecf20Sopenharmony_ciextern void load_fpu(const struct fpu_struct *fpregs);
178c2ecf20Sopenharmony_ciextern bool do_fpu_exception(unsigned int subtype, struct pt_regs *regs);
188c2ecf20Sopenharmony_ciextern int do_fpuemu(struct pt_regs *regs, struct fpu_struct *fpu);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define test_tsk_fpu(regs)	(regs->fucop_ctl & FUCOP_CTL_mskCP0EN)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * Initially load the FPU with signalling NANS.  This bit pattern
248c2ecf20Sopenharmony_ci * has the property that no matter whether considered as single or as
258c2ecf20Sopenharmony_ci * double precision, it still represents a signalling NAN.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define sNAN64    0xFFFFFFFFFFFFFFFFULL
298c2ecf20Sopenharmony_ci#define sNAN32    0xFFFFFFFFUL
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Denormalized number is unsupported by nds32 FPU. Hence the operation
348c2ecf20Sopenharmony_ci * is treated as underflow cases when the final result is a denormalized
358c2ecf20Sopenharmony_ci * number. To enhance precision, underflow exception trap should be
368c2ecf20Sopenharmony_ci * enabled by default and kerenl will re-execute it by fpu emulator
378c2ecf20Sopenharmony_ci * when getting underflow exception.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define FPCSR_INIT  (FPCSR_mskUDFE | FPCSR_mskIEXE)
408c2ecf20Sopenharmony_ci#else
418c2ecf20Sopenharmony_ci#define FPCSR_INIT  0x0UL
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciextern const struct fpu_struct init_fpuregs;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void disable_ptreg_fpu(struct pt_regs *regs)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	regs->fucop_ctl &= ~FUCOP_CTL_mskCP0EN;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic inline void enable_ptreg_fpu(struct pt_regs *regs)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	regs->fucop_ctl |= FUCOP_CTL_mskCP0EN;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline void enable_fpu(void)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned long fucop_ctl;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	fucop_ctl = __nds32__mfsr(NDS32_SR_FUCOP_CTL) | FUCOP_CTL_mskCP0EN;
618c2ecf20Sopenharmony_ci	__nds32__mtsr(fucop_ctl, NDS32_SR_FUCOP_CTL);
628c2ecf20Sopenharmony_ci	__nds32__isb();
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic inline void disable_fpu(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned long fucop_ctl;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	fucop_ctl = __nds32__mfsr(NDS32_SR_FUCOP_CTL) & ~FUCOP_CTL_mskCP0EN;
708c2ecf20Sopenharmony_ci	__nds32__mtsr(fucop_ctl, NDS32_SR_FUCOP_CTL);
718c2ecf20Sopenharmony_ci	__nds32__isb();
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline void lose_fpu(void)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	preempt_disable();
778c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_LAZY_FPU)
788c2ecf20Sopenharmony_ci	if (last_task_used_math == current) {
798c2ecf20Sopenharmony_ci		last_task_used_math = NULL;
808c2ecf20Sopenharmony_ci#else
818c2ecf20Sopenharmony_ci	if (test_tsk_fpu(task_pt_regs(current))) {
828c2ecf20Sopenharmony_ci#endif
838c2ecf20Sopenharmony_ci		save_fpu(current);
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci	disable_ptreg_fpu(task_pt_regs(current));
868c2ecf20Sopenharmony_ci	preempt_enable();
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic inline void own_fpu(void)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	preempt_disable();
928c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_LAZY_FPU)
938c2ecf20Sopenharmony_ci	if (last_task_used_math != current) {
948c2ecf20Sopenharmony_ci		if (last_task_used_math != NULL)
958c2ecf20Sopenharmony_ci			save_fpu(last_task_used_math);
968c2ecf20Sopenharmony_ci		load_fpu(&current->thread.fpu);
978c2ecf20Sopenharmony_ci		last_task_used_math = current;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci#else
1008c2ecf20Sopenharmony_ci	if (!test_tsk_fpu(task_pt_regs(current))) {
1018c2ecf20Sopenharmony_ci		load_fpu(&current->thread.fpu);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci#endif
1048c2ecf20Sopenharmony_ci	enable_ptreg_fpu(task_pt_regs(current));
1058c2ecf20Sopenharmony_ci	preempt_enable();
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#if !IS_ENABLED(CONFIG_LAZY_FPU)
1098c2ecf20Sopenharmony_cistatic inline void unlazy_fpu(struct task_struct *tsk)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	preempt_disable();
1128c2ecf20Sopenharmony_ci	if (test_tsk_fpu(task_pt_regs(tsk)))
1138c2ecf20Sopenharmony_ci		save_fpu(tsk);
1148c2ecf20Sopenharmony_ci	preempt_enable();
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci#endif /* !CONFIG_LAZY_FPU */
1178c2ecf20Sopenharmony_cistatic inline void clear_fpu(struct pt_regs *regs)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	preempt_disable();
1208c2ecf20Sopenharmony_ci	if (test_tsk_fpu(regs))
1218c2ecf20Sopenharmony_ci		disable_ptreg_fpu(regs);
1228c2ecf20Sopenharmony_ci	preempt_enable();
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci#endif /* CONFIG_FPU */
1258c2ecf20Sopenharmony_ci#endif /* __ASSEMBLY__ */
1268c2ecf20Sopenharmony_ci#endif /* __ASM_NDS32_FPU_H */
127