18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Author: Huacai Chen <chenhuacai@loongson.cn> 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Loongson Technology Corporation Limited 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#ifndef _ASM_FPU_H 78c2ecf20Sopenharmony_ci#define _ASM_FPU_H 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 118c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 128c2ecf20Sopenharmony_ci#include <linux/thread_info.h> 138c2ecf20Sopenharmony_ci#include <linux/bitops.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/cpu.h> 168c2ecf20Sopenharmony_ci#include <asm/cpu-features.h> 178c2ecf20Sopenharmony_ci#include <asm/current.h> 188c2ecf20Sopenharmony_ci#include <asm/inst.h> 198c2ecf20Sopenharmony_ci#include <asm/loongarchregs.h> 208c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 218c2ecf20Sopenharmony_ci#include <asm/processor.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct sigcontext; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciextern void kernel_fpu_begin(void); 268c2ecf20Sopenharmony_ciextern void kernel_fpu_end(void); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciextern void _init_fpu(unsigned int); 298c2ecf20Sopenharmony_ciextern void _save_fp(struct loongarch_fpu *); 308c2ecf20Sopenharmony_ciextern void _restore_fp(struct loongarch_fpu *); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciextern void _save_lsx(struct loongarch_fpu *fpu); 338c2ecf20Sopenharmony_ciextern void _restore_lsx(struct loongarch_fpu *fpu); 348c2ecf20Sopenharmony_ciextern void _init_lsx_upper(void); 358c2ecf20Sopenharmony_ciextern void _restore_lsx_upper(struct loongarch_fpu *fpu); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciextern void _save_lasx(struct loongarch_fpu *fpu); 388c2ecf20Sopenharmony_ciextern void _restore_lasx(struct loongarch_fpu *fpu); 398c2ecf20Sopenharmony_ciextern void _init_lasx_upper(void); 408c2ecf20Sopenharmony_ciextern void _restore_lasx_upper(struct loongarch_fpu *fpu); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic inline void enable_lsx(void); 438c2ecf20Sopenharmony_cistatic inline void disable_lsx(void); 448c2ecf20Sopenharmony_cistatic inline void save_lsx(struct task_struct *t); 458c2ecf20Sopenharmony_cistatic inline void restore_lsx(struct task_struct *t); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline void enable_lasx(void); 488c2ecf20Sopenharmony_cistatic inline void disable_lasx(void); 498c2ecf20Sopenharmony_cistatic inline void save_lasx(struct task_struct *t); 508c2ecf20Sopenharmony_cistatic inline void restore_lasx(struct task_struct *t); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Mask the FCSR Cause bits according to the Enable bits, observing 548c2ecf20Sopenharmony_ci * that Unimplemented is always enabled. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistatic inline unsigned long mask_fcsr_x(unsigned long fcsr) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return fcsr & ((fcsr & FPU_CSR_ALL_E) << 598c2ecf20Sopenharmony_ci (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E))); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic inline int is_fp_enabled(void) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_FPEN) ? 658c2ecf20Sopenharmony_ci 1 : 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline int is_lsx_enabled(void) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (!cpu_has_lsx) 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LSXEN) ? 748c2ecf20Sopenharmony_ci 1 : 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline int is_lasx_enabled(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (!cpu_has_lasx) 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LASXEN) ? 838c2ecf20Sopenharmony_ci 1 : 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline int is_simd_enabled(void) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return is_lsx_enabled() | is_lasx_enabled(); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define enable_fpu() \ 928c2ecf20Sopenharmony_cido { \ 938c2ecf20Sopenharmony_ci set_csr_euen(CSR_EUEN_FPEN); \ 948c2ecf20Sopenharmony_ci} while (0) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define disable_fpu() \ 978c2ecf20Sopenharmony_cido { \ 988c2ecf20Sopenharmony_ci clear_csr_euen(CSR_EUEN_FPEN); \ 998c2ecf20Sopenharmony_ci} while (0) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline int is_fpu_owner(void) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci return test_thread_flag(TIF_USEDFPU); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline void __own_fpu(void) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci enable_fpu(); 1118c2ecf20Sopenharmony_ci set_thread_flag(TIF_USEDFPU); 1128c2ecf20Sopenharmony_ci KSTK_EUEN(current) |= CSR_EUEN_FPEN; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline void own_fpu_inatomic(int restore) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci if (cpu_has_fpu && !is_fpu_owner()) { 1188c2ecf20Sopenharmony_ci __own_fpu(); 1198c2ecf20Sopenharmony_ci if (restore) 1208c2ecf20Sopenharmony_ci _restore_fp(¤t->thread.fpu); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic inline void own_fpu(int restore) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci preempt_disable(); 1278c2ecf20Sopenharmony_ci own_fpu_inatomic(restore); 1288c2ecf20Sopenharmony_ci preempt_enable(); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void lose_fpu_inatomic(int save, struct task_struct *tsk) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci if (is_fpu_owner()) { 1348c2ecf20Sopenharmony_ci if (!is_simd_enabled()) { 1358c2ecf20Sopenharmony_ci if (save) 1368c2ecf20Sopenharmony_ci _save_fp(&tsk->thread.fpu); 1378c2ecf20Sopenharmony_ci disable_fpu(); 1388c2ecf20Sopenharmony_ci } else { 1398c2ecf20Sopenharmony_ci if (save) { 1408c2ecf20Sopenharmony_ci if (!is_lasx_enabled()) 1418c2ecf20Sopenharmony_ci save_lsx(tsk); 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci save_lasx(tsk); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci disable_fpu(); 1468c2ecf20Sopenharmony_ci disable_lsx(); 1478c2ecf20Sopenharmony_ci disable_lasx(); 1488c2ecf20Sopenharmony_ci clear_tsk_thread_flag(tsk, TIF_USEDSIMD); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci clear_tsk_thread_flag(tsk, TIF_USEDFPU); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci KSTK_EUEN(tsk) &= ~(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic inline void lose_fpu(int save) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci preempt_disable(); 1588c2ecf20Sopenharmony_ci lose_fpu_inatomic(save, current); 1598c2ecf20Sopenharmony_ci preempt_enable(); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic inline void init_fpu(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci unsigned int fcsr = current->thread.fpu.fcsr; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci __own_fpu(); 1678c2ecf20Sopenharmony_ci _init_fpu(fcsr); 1688c2ecf20Sopenharmony_ci set_used_math(); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline void save_fp(struct task_struct *tsk) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci if (cpu_has_fpu) 1748c2ecf20Sopenharmony_ci _save_fp(&tsk->thread.fpu); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic inline void restore_fp(struct task_struct *tsk) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (cpu_has_fpu) 1808c2ecf20Sopenharmony_ci _restore_fp(&tsk->thread.fpu); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic inline void save_fpu_regs(struct task_struct *tsk) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned int euen; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (tsk == current) { 1888c2ecf20Sopenharmony_ci preempt_disable(); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci euen = csr_read32(LOONGARCH_CSR_EUEN); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LASX 1938c2ecf20Sopenharmony_ci if (euen & CSR_EUEN_LASXEN) 1948c2ecf20Sopenharmony_ci _save_lasx(¤t->thread.fpu); 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci#endif 1978c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LSX 1988c2ecf20Sopenharmony_ci if (euen & CSR_EUEN_LSXEN) 1998c2ecf20Sopenharmony_ci _save_lsx(¤t->thread.fpu); 2008c2ecf20Sopenharmony_ci else 2018c2ecf20Sopenharmony_ci#endif 2028c2ecf20Sopenharmony_ci if (euen & CSR_EUEN_FPEN) 2038c2ecf20Sopenharmony_ci _save_fp(¤t->thread.fpu); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci preempt_enable(); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic inline int is_simd_owner(void) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci return test_thread_flag(TIF_USEDSIMD); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LSX 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic inline void enable_lsx(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2198c2ecf20Sopenharmony_ci csr_xchg32(CSR_EUEN_LSXEN, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline void disable_lsx(void) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2258c2ecf20Sopenharmony_ci csr_xchg32(0, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic inline void save_lsx(struct task_struct *t) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2318c2ecf20Sopenharmony_ci _save_lsx(&t->thread.fpu); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic inline void restore_lsx(struct task_struct *t) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2378c2ecf20Sopenharmony_ci _restore_lsx(&t->thread.fpu); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic inline void init_lsx_upper(void) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2438c2ecf20Sopenharmony_ci _init_lsx_upper(); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic inline void restore_lsx_upper(struct task_struct *t) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (cpu_has_lsx) 2498c2ecf20Sopenharmony_ci _restore_lsx_upper(&t->thread.fpu); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci#else 2538c2ecf20Sopenharmony_cistatic inline void enable_lsx(void) {} 2548c2ecf20Sopenharmony_cistatic inline void disable_lsx(void) {} 2558c2ecf20Sopenharmony_cistatic inline void save_lsx(struct task_struct *t) {} 2568c2ecf20Sopenharmony_cistatic inline void restore_lsx(struct task_struct *t) {} 2578c2ecf20Sopenharmony_cistatic inline void init_lsx_upper(void) {} 2588c2ecf20Sopenharmony_cistatic inline void restore_lsx_upper(struct task_struct *t) {} 2598c2ecf20Sopenharmony_ci#endif 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LASX 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic inline void enable_lasx(void) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2678c2ecf20Sopenharmony_ci csr_xchg32(CSR_EUEN_LASXEN, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic inline void disable_lasx(void) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2738c2ecf20Sopenharmony_ci csr_xchg32(0, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic inline void save_lasx(struct task_struct *t) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2798c2ecf20Sopenharmony_ci _save_lasx(&t->thread.fpu); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic inline void restore_lasx(struct task_struct *t) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2858c2ecf20Sopenharmony_ci _restore_lasx(&t->thread.fpu); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic inline void init_lasx_upper(void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2918c2ecf20Sopenharmony_ci _init_lasx_upper(); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic inline void restore_lasx_upper(struct task_struct *t) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (cpu_has_lasx) 2978c2ecf20Sopenharmony_ci _restore_lasx_upper(&t->thread.fpu); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#else 3018c2ecf20Sopenharmony_cistatic inline void enable_lasx(void) {} 3028c2ecf20Sopenharmony_cistatic inline void disable_lasx(void) {} 3038c2ecf20Sopenharmony_cistatic inline void save_lasx(struct task_struct *t) {} 3048c2ecf20Sopenharmony_cistatic inline void restore_lasx(struct task_struct *t) {} 3058c2ecf20Sopenharmony_cistatic inline void init_lasx_upper(void) {} 3068c2ecf20Sopenharmony_cistatic inline void restore_lasx_upper(struct task_struct *t) {} 3078c2ecf20Sopenharmony_ci#endif 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic inline int thread_lsx_context_live(void) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (!cpu_has_lsx) 3128c2ecf20Sopenharmony_ci return 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return test_thread_flag(TIF_LSX_CTX_LIVE); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic inline int thread_lasx_context_live(void) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci if (!cpu_has_lasx) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return test_thread_flag(TIF_LASX_CTX_LIVE); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci#endif /* _ASM_FPU_H */ 326