xref: /linux/arch/loongarch/include/asm/fpu.h (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1803b0fc5SHuacai Chen /* SPDX-License-Identifier: GPL-2.0 */
2803b0fc5SHuacai Chen /*
3803b0fc5SHuacai Chen  * Author: Huacai Chen <chenhuacai@loongson.cn>
4803b0fc5SHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5803b0fc5SHuacai Chen  */
6803b0fc5SHuacai Chen #ifndef _ASM_FPU_H
7803b0fc5SHuacai Chen #define _ASM_FPU_H
8803b0fc5SHuacai Chen 
9803b0fc5SHuacai Chen #include <linux/sched.h>
10803b0fc5SHuacai Chen #include <linux/sched/task_stack.h>
11803b0fc5SHuacai Chen #include <linux/ptrace.h>
12803b0fc5SHuacai Chen #include <linux/thread_info.h>
13803b0fc5SHuacai Chen #include <linux/bitops.h>
14803b0fc5SHuacai Chen 
15803b0fc5SHuacai Chen #include <asm/cpu.h>
16803b0fc5SHuacai Chen #include <asm/cpu-features.h>
17803b0fc5SHuacai Chen #include <asm/current.h>
18803b0fc5SHuacai Chen #include <asm/loongarch.h>
19803b0fc5SHuacai Chen #include <asm/processor.h>
20803b0fc5SHuacai Chen #include <asm/ptrace.h>
21803b0fc5SHuacai Chen 
22803b0fc5SHuacai Chen struct sigcontext;
23803b0fc5SHuacai Chen 
24*372f6623SSamuel Holland #define kernel_fpu_available() cpu_has_fpu
252b3bd32eSHuacai Chen extern void kernel_fpu_begin(void);
262b3bd32eSHuacai Chen extern void kernel_fpu_end(void);
272b3bd32eSHuacai Chen 
28803b0fc5SHuacai Chen extern void _init_fpu(unsigned int);
29803b0fc5SHuacai Chen extern void _save_fp(struct loongarch_fpu *);
30803b0fc5SHuacai Chen extern void _restore_fp(struct loongarch_fpu *);
31803b0fc5SHuacai Chen 
3261650023SHuacai Chen extern void _save_lsx(struct loongarch_fpu *fpu);
3361650023SHuacai Chen extern void _restore_lsx(struct loongarch_fpu *fpu);
3461650023SHuacai Chen extern void _init_lsx_upper(void);
3561650023SHuacai Chen extern void _restore_lsx_upper(struct loongarch_fpu *fpu);
3661650023SHuacai Chen 
3761650023SHuacai Chen extern void _save_lasx(struct loongarch_fpu *fpu);
3861650023SHuacai Chen extern void _restore_lasx(struct loongarch_fpu *fpu);
3961650023SHuacai Chen extern void _init_lasx_upper(void);
4061650023SHuacai Chen extern void _restore_lasx_upper(struct loongarch_fpu *fpu);
4161650023SHuacai Chen 
4261650023SHuacai Chen static inline void enable_lsx(void);
4361650023SHuacai Chen static inline void disable_lsx(void);
4461650023SHuacai Chen static inline void save_lsx(struct task_struct *t);
4561650023SHuacai Chen static inline void restore_lsx(struct task_struct *t);
4661650023SHuacai Chen 
4761650023SHuacai Chen static inline void enable_lasx(void);
4861650023SHuacai Chen static inline void disable_lasx(void);
4961650023SHuacai Chen static inline void save_lasx(struct task_struct *t);
5061650023SHuacai Chen static inline void restore_lasx(struct task_struct *t);
5161650023SHuacai Chen 
52803b0fc5SHuacai Chen /*
53803b0fc5SHuacai Chen  * Mask the FCSR Cause bits according to the Enable bits, observing
54803b0fc5SHuacai Chen  * that Unimplemented is always enabled.
55803b0fc5SHuacai Chen  */
56803b0fc5SHuacai Chen static inline unsigned long mask_fcsr_x(unsigned long fcsr)
57803b0fc5SHuacai Chen {
58803b0fc5SHuacai Chen 	return fcsr & ((fcsr & FPU_CSR_ALL_E) <<
59803b0fc5SHuacai Chen 			(ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E)));
60803b0fc5SHuacai Chen }
61803b0fc5SHuacai Chen 
62803b0fc5SHuacai Chen static inline int is_fp_enabled(void)
63803b0fc5SHuacai Chen {
64803b0fc5SHuacai Chen 	return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_FPEN) ?
65803b0fc5SHuacai Chen 		1 : 0;
66803b0fc5SHuacai Chen }
67803b0fc5SHuacai Chen 
6861650023SHuacai Chen static inline int is_lsx_enabled(void)
6961650023SHuacai Chen {
7061650023SHuacai Chen 	if (!cpu_has_lsx)
7161650023SHuacai Chen 		return 0;
7261650023SHuacai Chen 
7361650023SHuacai Chen 	return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LSXEN) ?
7461650023SHuacai Chen 		1 : 0;
7561650023SHuacai Chen }
7661650023SHuacai Chen 
7761650023SHuacai Chen static inline int is_lasx_enabled(void)
7861650023SHuacai Chen {
7961650023SHuacai Chen 	if (!cpu_has_lasx)
8061650023SHuacai Chen 		return 0;
8161650023SHuacai Chen 
8261650023SHuacai Chen 	return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LASXEN) ?
8361650023SHuacai Chen 		1 : 0;
8461650023SHuacai Chen }
8561650023SHuacai Chen 
8661650023SHuacai Chen static inline int is_simd_enabled(void)
8761650023SHuacai Chen {
8861650023SHuacai Chen 	return is_lsx_enabled() | is_lasx_enabled();
8961650023SHuacai Chen }
9061650023SHuacai Chen 
91803b0fc5SHuacai Chen #define enable_fpu()		set_csr_euen(CSR_EUEN_FPEN)
92803b0fc5SHuacai Chen 
93803b0fc5SHuacai Chen #define disable_fpu()		clear_csr_euen(CSR_EUEN_FPEN)
94803b0fc5SHuacai Chen 
95803b0fc5SHuacai Chen #define clear_fpu_owner()	clear_thread_flag(TIF_USEDFPU)
96803b0fc5SHuacai Chen 
97803b0fc5SHuacai Chen static inline int is_fpu_owner(void)
98803b0fc5SHuacai Chen {
99803b0fc5SHuacai Chen 	return test_thread_flag(TIF_USEDFPU);
100803b0fc5SHuacai Chen }
101803b0fc5SHuacai Chen 
102803b0fc5SHuacai Chen static inline void __own_fpu(void)
103803b0fc5SHuacai Chen {
104803b0fc5SHuacai Chen 	enable_fpu();
105803b0fc5SHuacai Chen 	set_thread_flag(TIF_USEDFPU);
106803b0fc5SHuacai Chen 	KSTK_EUEN(current) |= CSR_EUEN_FPEN;
107803b0fc5SHuacai Chen }
108803b0fc5SHuacai Chen 
109803b0fc5SHuacai Chen static inline void own_fpu_inatomic(int restore)
110803b0fc5SHuacai Chen {
111803b0fc5SHuacai Chen 	if (cpu_has_fpu && !is_fpu_owner()) {
112803b0fc5SHuacai Chen 		__own_fpu();
113803b0fc5SHuacai Chen 		if (restore)
114803b0fc5SHuacai Chen 			_restore_fp(&current->thread.fpu);
115803b0fc5SHuacai Chen 	}
116803b0fc5SHuacai Chen }
117803b0fc5SHuacai Chen 
118803b0fc5SHuacai Chen static inline void own_fpu(int restore)
119803b0fc5SHuacai Chen {
120803b0fc5SHuacai Chen 	preempt_disable();
121803b0fc5SHuacai Chen 	own_fpu_inatomic(restore);
122803b0fc5SHuacai Chen 	preempt_enable();
123803b0fc5SHuacai Chen }
124803b0fc5SHuacai Chen 
125803b0fc5SHuacai Chen static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
126803b0fc5SHuacai Chen {
127803b0fc5SHuacai Chen 	if (is_fpu_owner()) {
12861650023SHuacai Chen 		if (!is_simd_enabled()) {
129803b0fc5SHuacai Chen 			if (save)
130803b0fc5SHuacai Chen 				_save_fp(&tsk->thread.fpu);
131803b0fc5SHuacai Chen 			disable_fpu();
13261650023SHuacai Chen 		} else {
13361650023SHuacai Chen 			if (save) {
13461650023SHuacai Chen 				if (!is_lasx_enabled())
13561650023SHuacai Chen 					save_lsx(tsk);
13661650023SHuacai Chen 				else
13761650023SHuacai Chen 					save_lasx(tsk);
13861650023SHuacai Chen 			}
13961650023SHuacai Chen 			disable_fpu();
14061650023SHuacai Chen 			disable_lsx();
14161650023SHuacai Chen 			disable_lasx();
14261650023SHuacai Chen 			clear_tsk_thread_flag(tsk, TIF_USEDSIMD);
14361650023SHuacai Chen 		}
144803b0fc5SHuacai Chen 		clear_tsk_thread_flag(tsk, TIF_USEDFPU);
145803b0fc5SHuacai Chen 	}
146803b0fc5SHuacai Chen 	KSTK_EUEN(tsk) &= ~(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN);
147803b0fc5SHuacai Chen }
148803b0fc5SHuacai Chen 
149803b0fc5SHuacai Chen static inline void lose_fpu(int save)
150803b0fc5SHuacai Chen {
151803b0fc5SHuacai Chen 	preempt_disable();
152803b0fc5SHuacai Chen 	lose_fpu_inatomic(save, current);
153803b0fc5SHuacai Chen 	preempt_enable();
154803b0fc5SHuacai Chen }
155803b0fc5SHuacai Chen 
156803b0fc5SHuacai Chen static inline void init_fpu(void)
157803b0fc5SHuacai Chen {
158803b0fc5SHuacai Chen 	unsigned int fcsr = current->thread.fpu.fcsr;
159803b0fc5SHuacai Chen 
160803b0fc5SHuacai Chen 	__own_fpu();
161803b0fc5SHuacai Chen 	_init_fpu(fcsr);
162803b0fc5SHuacai Chen 	set_used_math();
163803b0fc5SHuacai Chen }
164803b0fc5SHuacai Chen 
165803b0fc5SHuacai Chen static inline void save_fp(struct task_struct *tsk)
166803b0fc5SHuacai Chen {
167803b0fc5SHuacai Chen 	if (cpu_has_fpu)
168803b0fc5SHuacai Chen 		_save_fp(&tsk->thread.fpu);
169803b0fc5SHuacai Chen }
170803b0fc5SHuacai Chen 
171803b0fc5SHuacai Chen static inline void restore_fp(struct task_struct *tsk)
172803b0fc5SHuacai Chen {
173803b0fc5SHuacai Chen 	if (cpu_has_fpu)
174803b0fc5SHuacai Chen 		_restore_fp(&tsk->thread.fpu);
175803b0fc5SHuacai Chen }
176803b0fc5SHuacai Chen 
177656f9aecSHuacai Chen static inline void save_fpu_regs(struct task_struct *tsk)
178803b0fc5SHuacai Chen {
179656f9aecSHuacai Chen 	unsigned int euen;
180656f9aecSHuacai Chen 
181803b0fc5SHuacai Chen 	if (tsk == current) {
182803b0fc5SHuacai Chen 		preempt_disable();
183656f9aecSHuacai Chen 
184656f9aecSHuacai Chen 		euen = csr_read32(LOONGARCH_CSR_EUEN);
185656f9aecSHuacai Chen 
186656f9aecSHuacai Chen #ifdef CONFIG_CPU_HAS_LASX
187656f9aecSHuacai Chen 		if (euen & CSR_EUEN_LASXEN)
188656f9aecSHuacai Chen 			_save_lasx(&current->thread.fpu);
189656f9aecSHuacai Chen 		else
190656f9aecSHuacai Chen #endif
191656f9aecSHuacai Chen #ifdef CONFIG_CPU_HAS_LSX
192656f9aecSHuacai Chen 		if (euen & CSR_EUEN_LSXEN)
193656f9aecSHuacai Chen 			_save_lsx(&current->thread.fpu);
194656f9aecSHuacai Chen 		else
195656f9aecSHuacai Chen #endif
196656f9aecSHuacai Chen 		if (euen & CSR_EUEN_FPEN)
197803b0fc5SHuacai Chen 			_save_fp(&current->thread.fpu);
198656f9aecSHuacai Chen 
199803b0fc5SHuacai Chen 		preempt_enable();
200803b0fc5SHuacai Chen 	}
201803b0fc5SHuacai Chen }
202803b0fc5SHuacai Chen 
20361650023SHuacai Chen static inline int is_simd_owner(void)
20461650023SHuacai Chen {
20561650023SHuacai Chen 	return test_thread_flag(TIF_USEDSIMD);
20661650023SHuacai Chen }
20761650023SHuacai Chen 
20861650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LSX
20961650023SHuacai Chen 
21061650023SHuacai Chen static inline void enable_lsx(void)
21161650023SHuacai Chen {
21261650023SHuacai Chen 	if (cpu_has_lsx)
21361650023SHuacai Chen 		csr_xchg32(CSR_EUEN_LSXEN, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN);
21461650023SHuacai Chen }
21561650023SHuacai Chen 
21661650023SHuacai Chen static inline void disable_lsx(void)
21761650023SHuacai Chen {
21861650023SHuacai Chen 	if (cpu_has_lsx)
21961650023SHuacai Chen 		csr_xchg32(0, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN);
22061650023SHuacai Chen }
22161650023SHuacai Chen 
22261650023SHuacai Chen static inline void save_lsx(struct task_struct *t)
22361650023SHuacai Chen {
22461650023SHuacai Chen 	if (cpu_has_lsx)
22561650023SHuacai Chen 		_save_lsx(&t->thread.fpu);
22661650023SHuacai Chen }
22761650023SHuacai Chen 
22861650023SHuacai Chen static inline void restore_lsx(struct task_struct *t)
22961650023SHuacai Chen {
23061650023SHuacai Chen 	if (cpu_has_lsx)
23161650023SHuacai Chen 		_restore_lsx(&t->thread.fpu);
23261650023SHuacai Chen }
23361650023SHuacai Chen 
23461650023SHuacai Chen static inline void init_lsx_upper(void)
23561650023SHuacai Chen {
2361e74ae32SHuacai Chen 	if (cpu_has_lsx)
23761650023SHuacai Chen 		_init_lsx_upper();
23861650023SHuacai Chen }
23961650023SHuacai Chen 
24061650023SHuacai Chen static inline void restore_lsx_upper(struct task_struct *t)
24161650023SHuacai Chen {
24261650023SHuacai Chen 	if (cpu_has_lsx)
24361650023SHuacai Chen 		_restore_lsx_upper(&t->thread.fpu);
24461650023SHuacai Chen }
24561650023SHuacai Chen 
24661650023SHuacai Chen #else
24761650023SHuacai Chen static inline void enable_lsx(void) {}
24861650023SHuacai Chen static inline void disable_lsx(void) {}
24961650023SHuacai Chen static inline void save_lsx(struct task_struct *t) {}
25061650023SHuacai Chen static inline void restore_lsx(struct task_struct *t) {}
25161650023SHuacai Chen static inline void init_lsx_upper(void) {}
25261650023SHuacai Chen static inline void restore_lsx_upper(struct task_struct *t) {}
25361650023SHuacai Chen #endif
25461650023SHuacai Chen 
25561650023SHuacai Chen #ifdef CONFIG_CPU_HAS_LASX
25661650023SHuacai Chen 
25761650023SHuacai Chen static inline void enable_lasx(void)
25861650023SHuacai Chen {
25961650023SHuacai Chen 
26061650023SHuacai Chen 	if (cpu_has_lasx)
26161650023SHuacai Chen 		csr_xchg32(CSR_EUEN_LASXEN, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN);
26261650023SHuacai Chen }
26361650023SHuacai Chen 
26461650023SHuacai Chen static inline void disable_lasx(void)
26561650023SHuacai Chen {
26661650023SHuacai Chen 	if (cpu_has_lasx)
26761650023SHuacai Chen 		csr_xchg32(0, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN);
26861650023SHuacai Chen }
26961650023SHuacai Chen 
27061650023SHuacai Chen static inline void save_lasx(struct task_struct *t)
27161650023SHuacai Chen {
27261650023SHuacai Chen 	if (cpu_has_lasx)
27361650023SHuacai Chen 		_save_lasx(&t->thread.fpu);
27461650023SHuacai Chen }
27561650023SHuacai Chen 
27661650023SHuacai Chen static inline void restore_lasx(struct task_struct *t)
27761650023SHuacai Chen {
27861650023SHuacai Chen 	if (cpu_has_lasx)
27961650023SHuacai Chen 		_restore_lasx(&t->thread.fpu);
28061650023SHuacai Chen }
28161650023SHuacai Chen 
28261650023SHuacai Chen static inline void init_lasx_upper(void)
28361650023SHuacai Chen {
28461650023SHuacai Chen 	if (cpu_has_lasx)
28561650023SHuacai Chen 		_init_lasx_upper();
28661650023SHuacai Chen }
28761650023SHuacai Chen 
28861650023SHuacai Chen static inline void restore_lasx_upper(struct task_struct *t)
28961650023SHuacai Chen {
29061650023SHuacai Chen 	if (cpu_has_lasx)
29161650023SHuacai Chen 		_restore_lasx_upper(&t->thread.fpu);
29261650023SHuacai Chen }
29361650023SHuacai Chen 
29461650023SHuacai Chen #else
29561650023SHuacai Chen static inline void enable_lasx(void) {}
29661650023SHuacai Chen static inline void disable_lasx(void) {}
29761650023SHuacai Chen static inline void save_lasx(struct task_struct *t) {}
29861650023SHuacai Chen static inline void restore_lasx(struct task_struct *t) {}
29961650023SHuacai Chen static inline void init_lasx_upper(void) {}
30061650023SHuacai Chen static inline void restore_lasx_upper(struct task_struct *t) {}
30161650023SHuacai Chen #endif
30261650023SHuacai Chen 
30361650023SHuacai Chen static inline int thread_lsx_context_live(void)
30461650023SHuacai Chen {
3051e74ae32SHuacai Chen 	if (!cpu_has_lsx)
30661650023SHuacai Chen 		return 0;
30761650023SHuacai Chen 
30861650023SHuacai Chen 	return test_thread_flag(TIF_LSX_CTX_LIVE);
30961650023SHuacai Chen }
31061650023SHuacai Chen 
31161650023SHuacai Chen static inline int thread_lasx_context_live(void)
31261650023SHuacai Chen {
3131e74ae32SHuacai Chen 	if (!cpu_has_lasx)
31461650023SHuacai Chen 		return 0;
31561650023SHuacai Chen 
31661650023SHuacai Chen 	return test_thread_flag(TIF_LASX_CTX_LIVE);
31761650023SHuacai Chen }
31861650023SHuacai Chen 
319803b0fc5SHuacai Chen #endif /* _ASM_FPU_H */
320