xref: /linux/arch/riscv/kernel/fpu.S (revision 447e140e66fd226350b3ce86cffc965eaae4c856)
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