| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Copyright (C) 2012 Regents of the University of California |
| * Copyright (C) 2017 SiFive |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation, version 2. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/linkage.h> |
| |
| #include <asm/asm.h> |
| #include <asm/csr.h> |
| #include <asm/asm-offsets.h> |
| |
| SYM_FUNC_START(__fstate_save) |
| li a2, TASK_THREAD_F0 |
| add a0, a0, a2 |
| li t1, SR_FS |
| csrs CSR_STATUS, t1 |
| frcsr t0 |
| fsd f0, TASK_THREAD_F0_F0(a0) |
| fsd f1, TASK_THREAD_F1_F0(a0) |
| fsd f2, TASK_THREAD_F2_F0(a0) |
| fsd f3, TASK_THREAD_F3_F0(a0) |
| fsd f4, TASK_THREAD_F4_F0(a0) |
| fsd f5, TASK_THREAD_F5_F0(a0) |
| fsd f6, TASK_THREAD_F6_F0(a0) |
| fsd f7, TASK_THREAD_F7_F0(a0) |
| fsd f8, TASK_THREAD_F8_F0(a0) |
| fsd f9, TASK_THREAD_F9_F0(a0) |
| fsd f10, TASK_THREAD_F10_F0(a0) |
| fsd f11, TASK_THREAD_F11_F0(a0) |
| fsd f12, TASK_THREAD_F12_F0(a0) |
| fsd f13, TASK_THREAD_F13_F0(a0) |
| fsd f14, TASK_THREAD_F14_F0(a0) |
| fsd f15, TASK_THREAD_F15_F0(a0) |
| fsd f16, TASK_THREAD_F16_F0(a0) |
| fsd f17, TASK_THREAD_F17_F0(a0) |
| fsd f18, TASK_THREAD_F18_F0(a0) |
| fsd f19, TASK_THREAD_F19_F0(a0) |
| fsd f20, TASK_THREAD_F20_F0(a0) |
| fsd f21, TASK_THREAD_F21_F0(a0) |
| fsd f22, TASK_THREAD_F22_F0(a0) |
| fsd f23, TASK_THREAD_F23_F0(a0) |
| fsd f24, TASK_THREAD_F24_F0(a0) |
| fsd f25, TASK_THREAD_F25_F0(a0) |
| fsd f26, TASK_THREAD_F26_F0(a0) |
| fsd f27, TASK_THREAD_F27_F0(a0) |
| fsd f28, TASK_THREAD_F28_F0(a0) |
| fsd f29, TASK_THREAD_F29_F0(a0) |
| fsd f30, TASK_THREAD_F30_F0(a0) |
| fsd f31, TASK_THREAD_F31_F0(a0) |
| sw t0, TASK_THREAD_FCSR_F0(a0) |
| csrc CSR_STATUS, t1 |
| ret |
| SYM_FUNC_END(__fstate_save) |
| |
| SYM_FUNC_START(__fstate_restore) |
| li a2, TASK_THREAD_F0 |
| add a0, a0, a2 |
| li t1, SR_FS |
| lw t0, TASK_THREAD_FCSR_F0(a0) |
| csrs CSR_STATUS, t1 |
| fld f0, TASK_THREAD_F0_F0(a0) |
| fld f1, TASK_THREAD_F1_F0(a0) |
| fld f2, TASK_THREAD_F2_F0(a0) |
| fld f3, TASK_THREAD_F3_F0(a0) |
| fld f4, TASK_THREAD_F4_F0(a0) |
| fld f5, TASK_THREAD_F5_F0(a0) |
| fld f6, TASK_THREAD_F6_F0(a0) |
| fld f7, TASK_THREAD_F7_F0(a0) |
| fld f8, TASK_THREAD_F8_F0(a0) |
| fld f9, TASK_THREAD_F9_F0(a0) |
| fld f10, TASK_THREAD_F10_F0(a0) |
| fld f11, TASK_THREAD_F11_F0(a0) |
| fld f12, TASK_THREAD_F12_F0(a0) |
| fld f13, TASK_THREAD_F13_F0(a0) |
| fld f14, TASK_THREAD_F14_F0(a0) |
| fld f15, TASK_THREAD_F15_F0(a0) |
| fld f16, TASK_THREAD_F16_F0(a0) |
| fld f17, TASK_THREAD_F17_F0(a0) |
| fld f18, TASK_THREAD_F18_F0(a0) |
| fld f19, TASK_THREAD_F19_F0(a0) |
| fld f20, TASK_THREAD_F20_F0(a0) |
| fld f21, TASK_THREAD_F21_F0(a0) |
| fld f22, TASK_THREAD_F22_F0(a0) |
| fld f23, TASK_THREAD_F23_F0(a0) |
| fld f24, TASK_THREAD_F24_F0(a0) |
| fld f25, TASK_THREAD_F25_F0(a0) |
| fld f26, TASK_THREAD_F26_F0(a0) |
| fld f27, TASK_THREAD_F27_F0(a0) |
| fld f28, TASK_THREAD_F28_F0(a0) |
| fld f29, TASK_THREAD_F29_F0(a0) |
| fld f30, TASK_THREAD_F30_F0(a0) |
| fld f31, TASK_THREAD_F31_F0(a0) |
| fscsr t0 |
| csrc CSR_STATUS, t1 |
| ret |
| SYM_FUNC_END(__fstate_restore) |
| |
| #define get_f32(which) fmv.x.s a0, which; j 2f |
| #define put_f32(which) fmv.s.x which, a1; j 2f |
| #if __riscv_xlen == 64 |
| # define get_f64(which) fmv.x.d a0, which; j 2f |
| # define put_f64(which) fmv.d.x which, a1; j 2f |
| #else |
| # define get_f64(which) fsd which, 0(a1); j 2f |
| # define put_f64(which) fld which, 0(a1); j 2f |
| #endif |
| |
| .macro fp_access_prologue |
| /* |
| * Compute jump offset to store the correct FP register since we don't |
| * have indirect FP register access |
| */ |
| sll t0, a0, 3 |
| la t2, 1f |
| add t0, t0, t2 |
| li t1, SR_FS |
| csrs CSR_STATUS, t1 |
| jr t0 |
| 1: |
| .endm |
| |
| .macro fp_access_epilogue |
| 2: |
| csrc CSR_STATUS, t1 |
| ret |
| .endm |
| |
| #define fp_access_body(__access_func) \ |
| __access_func(f0); \ |
| __access_func(f1); \ |
| __access_func(f2); \ |
| __access_func(f3); \ |
| __access_func(f4); \ |
| __access_func(f5); \ |
| __access_func(f6); \ |
| __access_func(f7); \ |
| __access_func(f8); \ |
| __access_func(f9); \ |
| __access_func(f10); \ |
| __access_func(f11); \ |
| __access_func(f12); \ |
| __access_func(f13); \ |
| __access_func(f14); \ |
| __access_func(f15); \ |
| __access_func(f16); \ |
| __access_func(f17); \ |
| __access_func(f18); \ |
| __access_func(f19); \ |
| __access_func(f20); \ |
| __access_func(f21); \ |
| __access_func(f22); \ |
| __access_func(f23); \ |
| __access_func(f24); \ |
| __access_func(f25); \ |
| __access_func(f26); \ |
| __access_func(f27); \ |
| __access_func(f28); \ |
| __access_func(f29); \ |
| __access_func(f30); \ |
| __access_func(f31) |
| |
| |
| #ifdef CONFIG_RISCV_MISALIGNED |
| |
| /* |
| * Disable compressed instructions set to keep a constant offset between FP |
| * load/store/move instructions |
| */ |
| .option norvc |
| /* |
| * put_f32_reg - Set a FP register from a register containing the value |
| * a0 = FP register index to be set |
| * a1 = value to be loaded in the FP register |
| */ |
| SYM_FUNC_START(put_f32_reg) |
| fp_access_prologue |
| fp_access_body(put_f32) |
| fp_access_epilogue |
| SYM_FUNC_END(put_f32_reg) |
| |
| /* |
| * get_f32_reg - Get a FP register value and return it |
| * a0 = FP register index to be retrieved |
| */ |
| SYM_FUNC_START(get_f32_reg) |
| fp_access_prologue |
| fp_access_body(get_f32) |
| fp_access_epilogue |
| SYM_FUNC_END(get_f32_reg) |
| |
| /* |
| * put_f64_reg - Set a 64 bits FP register from a value or a pointer. |
| * a0 = FP register index to be set |
| * a1 = value/pointer to be loaded in the FP register (when xlen == 32 bits, we |
| * load the value to a pointer). |
| */ |
| SYM_FUNC_START(put_f64_reg) |
| fp_access_prologue |
| fp_access_body(put_f64) |
| fp_access_epilogue |
| SYM_FUNC_END(put_f64_reg) |
| |
| /* |
| * put_f64_reg - Get a 64 bits FP register value and returned it or store it to |
| * a pointer. |
| * a0 = FP register index to be retrieved |
| * a1 = If xlen == 32, pointer which should be loaded with the FP register value |
| * or unused if xlen == 64. In which case the FP register value is returned |
| * through a0 |
| */ |
| SYM_FUNC_START(get_f64_reg) |
| fp_access_prologue |
| fp_access_body(get_f64) |
| fp_access_epilogue |
| SYM_FUNC_END(get_f64_reg) |
| |
| #endif /* CONFIG_RISCV_MISALIGNED */ |