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 | |
22 | SYM_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 |
63 | SYM_FUNC_END(__fstate_save) |
64 | |
65 | SYM_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 |
106 | SYM_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 |
129 | 1: |
130 | .endm |
131 | |
132 | .macro fp_access_epilogue |
133 | 2: |
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 | */ |
185 | SYM_FUNC_START(put_f32_reg) |
186 | fp_access_prologue |
187 | fp_access_body(put_f32) |
188 | fp_access_epilogue |
189 | SYM_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 | */ |
195 | SYM_FUNC_START(get_f32_reg) |
196 | fp_access_prologue |
197 | fp_access_body(get_f32) |
198 | fp_access_epilogue |
199 | SYM_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 | */ |
207 | SYM_FUNC_START(put_f64_reg) |
208 | fp_access_prologue |
209 | fp_access_body(put_f64) |
210 | fp_access_epilogue |
211 | SYM_FUNC_END(put_f64_reg) |
212 | |
213 | /* |
214 | * put_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 | */ |
221 | SYM_FUNC_START(get_f64_reg) |
222 | fp_access_prologue |
223 | fp_access_body(get_f64) |
224 | fp_access_epilogue |
225 | SYM_FUNC_END(get_f64_reg) |
226 | |
227 | #endif /* CONFIG_RISCV_MISALIGNED */ |
228 | |