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(¤t->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(¤t->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(¤t->thread.fpu); 194656f9aecSHuacai Chen else 195656f9aecSHuacai Chen #endif 196656f9aecSHuacai Chen if (euen & CSR_EUEN_FPEN) 197803b0fc5SHuacai Chen _save_fp(¤t->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