1/* SPDX-License-Identifier: GPL-2.0 */ 2/* 3 * Copyright (C) 2012 Regents of the University of California 4 * Copyright (C) 2017 SiFive 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation, version 2. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16#include <linux/linkage.h> 17 18#include <asm/asm.h> 19#include <asm/csr.h> 20#include <asm/asm-offsets.h> 21 22SYM_FUNC_START(__fstate_save) 23 li a2, TASK_THREAD_F0 24 add a0, a0, a2 25 li t1, SR_FS 26 csrs CSR_STATUS, t1 27 frcsr t0 28 fsd f0, TASK_THREAD_F0_F0(a0) 29 fsd f1, TASK_THREAD_F1_F0(a0) 30 fsd f2, TASK_THREAD_F2_F0(a0) 31 fsd f3, TASK_THREAD_F3_F0(a0) 32 fsd f4, TASK_THREAD_F4_F0(a0) 33 fsd f5, TASK_THREAD_F5_F0(a0) 34 fsd f6, TASK_THREAD_F6_F0(a0) 35 fsd f7, TASK_THREAD_F7_F0(a0) 36 fsd f8, TASK_THREAD_F8_F0(a0) 37 fsd f9, TASK_THREAD_F9_F0(a0) 38 fsd f10, TASK_THREAD_F10_F0(a0) 39 fsd f11, TASK_THREAD_F11_F0(a0) 40 fsd f12, TASK_THREAD_F12_F0(a0) 41 fsd f13, TASK_THREAD_F13_F0(a0) 42 fsd f14, TASK_THREAD_F14_F0(a0) 43 fsd f15, TASK_THREAD_F15_F0(a0) 44 fsd f16, TASK_THREAD_F16_F0(a0) 45 fsd f17, TASK_THREAD_F17_F0(a0) 46 fsd f18, TASK_THREAD_F18_F0(a0) 47 fsd f19, TASK_THREAD_F19_F0(a0) 48 fsd f20, TASK_THREAD_F20_F0(a0) 49 fsd f21, TASK_THREAD_F21_F0(a0) 50 fsd f22, TASK_THREAD_F22_F0(a0) 51 fsd f23, TASK_THREAD_F23_F0(a0) 52 fsd f24, TASK_THREAD_F24_F0(a0) 53 fsd f25, TASK_THREAD_F25_F0(a0) 54 fsd f26, TASK_THREAD_F26_F0(a0) 55 fsd f27, TASK_THREAD_F27_F0(a0) 56 fsd f28, TASK_THREAD_F28_F0(a0) 57 fsd f29, TASK_THREAD_F29_F0(a0) 58 fsd f30, TASK_THREAD_F30_F0(a0) 59 fsd f31, TASK_THREAD_F31_F0(a0) 60 sw t0, TASK_THREAD_FCSR_F0(a0) 61 csrc CSR_STATUS, t1 62 ret 63SYM_FUNC_END(__fstate_save) 64 65SYM_FUNC_START(__fstate_restore) 66 li a2, TASK_THREAD_F0 67 add a0, a0, a2 68 li t1, SR_FS 69 lw t0, TASK_THREAD_FCSR_F0(a0) 70 csrs CSR_STATUS, t1 71 fld f0, TASK_THREAD_F0_F0(a0) 72 fld f1, TASK_THREAD_F1_F0(a0) 73 fld f2, TASK_THREAD_F2_F0(a0) 74 fld f3, TASK_THREAD_F3_F0(a0) 75 fld f4, TASK_THREAD_F4_F0(a0) 76 fld f5, TASK_THREAD_F5_F0(a0) 77 fld f6, TASK_THREAD_F6_F0(a0) 78 fld f7, TASK_THREAD_F7_F0(a0) 79 fld f8, TASK_THREAD_F8_F0(a0) 80 fld f9, TASK_THREAD_F9_F0(a0) 81 fld f10, TASK_THREAD_F10_F0(a0) 82 fld f11, TASK_THREAD_F11_F0(a0) 83 fld f12, TASK_THREAD_F12_F0(a0) 84 fld f13, TASK_THREAD_F13_F0(a0) 85 fld f14, TASK_THREAD_F14_F0(a0) 86 fld f15, TASK_THREAD_F15_F0(a0) 87 fld f16, TASK_THREAD_F16_F0(a0) 88 fld f17, TASK_THREAD_F17_F0(a0) 89 fld f18, TASK_THREAD_F18_F0(a0) 90 fld f19, TASK_THREAD_F19_F0(a0) 91 fld f20, TASK_THREAD_F20_F0(a0) 92 fld f21, TASK_THREAD_F21_F0(a0) 93 fld f22, TASK_THREAD_F22_F0(a0) 94 fld f23, TASK_THREAD_F23_F0(a0) 95 fld f24, TASK_THREAD_F24_F0(a0) 96 fld f25, TASK_THREAD_F25_F0(a0) 97 fld f26, TASK_THREAD_F26_F0(a0) 98 fld f27, TASK_THREAD_F27_F0(a0) 99 fld f28, TASK_THREAD_F28_F0(a0) 100 fld f29, TASK_THREAD_F29_F0(a0) 101 fld f30, TASK_THREAD_F30_F0(a0) 102 fld f31, TASK_THREAD_F31_F0(a0) 103 fscsr t0 104 csrc CSR_STATUS, t1 105 ret 106SYM_FUNC_END(__fstate_restore) 107 108#define get_f32(which) fmv.x.s a0, which; j 2f 109#define put_f32(which) fmv.s.x which, a1; j 2f 110#if __riscv_xlen == 64 111# define get_f64(which) fmv.x.d a0, which; j 2f 112# define put_f64(which) fmv.d.x which, a1; j 2f 113#else 114# define get_f64(which) fsd which, 0(a1); j 2f 115# define put_f64(which) fld which, 0(a1); j 2f 116#endif 117 118.macro fp_access_prologue 119 /* 120 * Compute jump offset to store the correct FP register since we don't 121 * have indirect FP register access 122 */ 123 sll t0, a0, 3 124 la t2, 1f 125 add t0, t0, t2 126 li t1, SR_FS 127 csrs CSR_STATUS, t1 128 jr t0 1291: 130.endm 131 132.macro fp_access_epilogue 1332: 134 csrc CSR_STATUS, t1 135 ret 136.endm 137 138#define fp_access_body(__access_func) \ 139 __access_func(f0); \ 140 __access_func(f1); \ 141 __access_func(f2); \ 142 __access_func(f3); \ 143 __access_func(f4); \ 144 __access_func(f5); \ 145 __access_func(f6); \ 146 __access_func(f7); \ 147 __access_func(f8); \ 148 __access_func(f9); \ 149 __access_func(f10); \ 150 __access_func(f11); \ 151 __access_func(f12); \ 152 __access_func(f13); \ 153 __access_func(f14); \ 154 __access_func(f15); \ 155 __access_func(f16); \ 156 __access_func(f17); \ 157 __access_func(f18); \ 158 __access_func(f19); \ 159 __access_func(f20); \ 160 __access_func(f21); \ 161 __access_func(f22); \ 162 __access_func(f23); \ 163 __access_func(f24); \ 164 __access_func(f25); \ 165 __access_func(f26); \ 166 __access_func(f27); \ 167 __access_func(f28); \ 168 __access_func(f29); \ 169 __access_func(f30); \ 170 __access_func(f31) 171 172 173#ifdef CONFIG_RISCV_MISALIGNED 174 175/* 176 * Disable compressed instructions set to keep a constant offset between FP 177 * load/store/move instructions 178 */ 179.option norvc 180/* 181 * put_f32_reg - Set a FP register from a register containing the value 182 * a0 = FP register index to be set 183 * a1 = value to be loaded in the FP register 184 */ 185SYM_FUNC_START(put_f32_reg) 186 fp_access_prologue 187 fp_access_body(put_f32) 188 fp_access_epilogue 189SYM_FUNC_END(put_f32_reg) 190 191/* 192 * get_f32_reg - Get a FP register value and return it 193 * a0 = FP register index to be retrieved 194 */ 195SYM_FUNC_START(get_f32_reg) 196 fp_access_prologue 197 fp_access_body(get_f32) 198 fp_access_epilogue 199SYM_FUNC_END(get_f32_reg) 200 201/* 202 * put_f64_reg - Set a 64 bits FP register from a value or a pointer. 203 * a0 = FP register index to be set 204 * a1 = value/pointer to be loaded in the FP register (when xlen == 32 bits, we 205 * load the value to a pointer). 206 */ 207SYM_FUNC_START(put_f64_reg) 208 fp_access_prologue 209 fp_access_body(put_f64) 210 fp_access_epilogue 211SYM_FUNC_END(put_f64_reg) 212 213/* 214 * get_f64_reg - Get a 64 bits FP register value and returned it or store it to 215 * a pointer. 216 * a0 = FP register index to be retrieved 217 * a1 = If xlen == 32, pointer which should be loaded with the FP register value 218 * or unused if xlen == 64. In which case the FP register value is returned 219 * through a0 220 */ 221SYM_FUNC_START(get_f64_reg) 222 fp_access_prologue 223 fp_access_body(get_f64) 224 fp_access_epilogue 225SYM_FUNC_END(get_f64_reg) 226 227#endif /* CONFIG_RISCV_MISALIGNED */ 228