1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * Floating-point, VMX/Altivec and VSX loads and stores
4 * for use in instruction emulation.
5 *
6 * Copyright 2010 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
7 */
8
9#include <asm/processor.h>
10#include <asm/ppc_asm.h>
11#include <asm/ppc-opcode.h>
12#include <asm/reg.h>
13#include <asm/asm-offsets.h>
14#include <asm/asm-compat.h>
15#include <linux/errno.h>
16
17#define STKFRM	(PPC_MIN_STKFRM + 16)
18
19/* Get the contents of frN into *p; N is in r3 and p is in r4. */
20_GLOBAL(get_fpr)
21	mflr	r0
22	mfmsr	r6
23	ori	r7, r6, MSR_FP
24	MTMSRD(r7)
25	isync
26	rlwinm	r3,r3,3,0xf8
27	bcl	20,31,1f
28reg = 0
29	.rept	32
30	stfd	reg, 0(r4)
31	b	2f
32reg = reg + 1
33	.endr
341:	mflr	r5
35	add	r5,r3,r5
36	mtctr	r5
37	mtlr	r0
38	bctr
392:	MTMSRD(r6)
40	isync
41	blr
42
43/* Put the contents of *p into frN; N is in r3 and p is in r4. */
44_GLOBAL(put_fpr)
45	mflr	r0
46	mfmsr	r6
47	ori	r7, r6, MSR_FP
48	MTMSRD(r7)
49	isync
50	rlwinm	r3,r3,3,0xf8
51	bcl	20,31,1f
52reg = 0
53	.rept	32
54	lfd	reg, 0(r4)
55	b	2f
56reg = reg + 1
57	.endr
581:	mflr	r5
59	add	r5,r3,r5
60	mtctr	r5
61	mtlr	r0
62	bctr
632:	MTMSRD(r6)
64	isync
65	blr
66
67#ifdef CONFIG_ALTIVEC
68/* Get the contents of vrN into *p; N is in r3 and p is in r4. */
69_GLOBAL(get_vr)
70	mflr	r0
71	mfmsr	r6
72	oris	r7, r6, MSR_VEC@h
73	MTMSRD(r7)
74	isync
75	rlwinm	r3,r3,3,0xf8
76	bcl	20,31,1f
77reg = 0
78	.rept	32
79	stvx	reg, 0, r4
80	b	2f
81reg = reg + 1
82	.endr
831:	mflr	r5
84	add	r5,r3,r5
85	mtctr	r5
86	mtlr	r0
87	bctr
882:	MTMSRD(r6)
89	isync
90	blr
91
92/* Put the contents of *p into vrN; N is in r3 and p is in r4. */
93_GLOBAL(put_vr)
94	mflr	r0
95	mfmsr	r6
96	oris	r7, r6, MSR_VEC@h
97	MTMSRD(r7)
98	isync
99	rlwinm	r3,r3,3,0xf8
100	bcl	20,31,1f
101reg = 0
102	.rept	32
103	lvx	reg, 0, r4
104	b	2f
105reg = reg + 1
106	.endr
1071:	mflr	r5
108	add	r5,r3,r5
109	mtctr	r5
110	mtlr	r0
111	bctr
1122:	MTMSRD(r6)
113	isync
114	blr
115#endif /* CONFIG_ALTIVEC */
116
117#ifdef CONFIG_VSX
118/* Get the contents of vsN into vs0; N is in r3. */
119_GLOBAL(get_vsr)
120	mflr	r0
121	rlwinm	r3,r3,3,0x1f8
122	bcl	20,31,1f
123	blr			/* vs0 is already in vs0 */
124	nop
125reg = 1
126	.rept	63
127	XXLOR(0,reg,reg)
128	blr
129reg = reg + 1
130	.endr
1311:	mflr	r5
132	add	r5,r3,r5
133	mtctr	r5
134	mtlr	r0
135	bctr
136
137/* Put the contents of vs0 into vsN; N is in r3. */
138_GLOBAL(put_vsr)
139	mflr	r0
140	rlwinm	r3,r3,3,0x1f8
141	bcl	20,31,1f
142	blr			/* v0 is already in v0 */
143	nop
144reg = 1
145	.rept	63
146	XXLOR(reg,0,0)
147	blr
148reg = reg + 1
149	.endr
1501:	mflr	r5
151	add	r5,r3,r5
152	mtctr	r5
153	mtlr	r0
154	bctr
155
156/* Load VSX reg N from vector doubleword *p.  N is in r3, p in r4. */
157_GLOBAL(load_vsrn)
158	PPC_STLU r1,-STKFRM(r1)
159	mflr	r0
160	PPC_STL	r0,STKFRM+PPC_LR_STKOFF(r1)
161	mfmsr	r6
162	oris	r7,r6,MSR_VSX@h
163	cmpwi	cr7,r3,0
164	li	r8,STKFRM-16
165	MTMSRD(r7)
166	isync
167	beq	cr7,1f
168	STXVD2X(0,R1,R8)
1691:	LXVD2X(0,R0,R4)
170#ifdef __LITTLE_ENDIAN__
171	XXSWAPD(0,0)
172#endif
173	beq	cr7,4f
174	bl	put_vsr
175	LXVD2X(0,R1,R8)
1764:	PPC_LL	r0,STKFRM+PPC_LR_STKOFF(r1)
177	mtlr	r0
178	MTMSRD(r6)
179	isync
180	addi	r1,r1,STKFRM
181	blr
182
183/* Store VSX reg N to vector doubleword *p.  N is in r3, p in r4. */
184_GLOBAL(store_vsrn)
185	PPC_STLU r1,-STKFRM(r1)
186	mflr	r0
187	PPC_STL	r0,STKFRM+PPC_LR_STKOFF(r1)
188	mfmsr	r6
189	oris	r7,r6,MSR_VSX@h
190	li	r8,STKFRM-16
191	MTMSRD(r7)
192	isync
193	STXVD2X(0,R1,R8)
194	bl	get_vsr
195#ifdef __LITTLE_ENDIAN__
196	XXSWAPD(0,0)
197#endif
198	STXVD2X(0,R0,R4)
199	LXVD2X(0,R1,R8)
200	PPC_LL	r0,STKFRM+PPC_LR_STKOFF(r1)
201	mtlr	r0
202	MTMSRD(r6)
203	isync
204	mr	r3,r9
205	addi	r1,r1,STKFRM
206	blr
207#endif /* CONFIG_VSX */
208
209/* Convert single-precision to double, without disturbing FPRs. */
210/* conv_sp_to_dp(float *sp, double *dp) */
211_GLOBAL(conv_sp_to_dp)
212	mfmsr	r6
213	ori	r7, r6, MSR_FP
214	MTMSRD(r7)
215	isync
216	stfd	fr0, -16(r1)
217	lfs	fr0, 0(r3)
218	stfd	fr0, 0(r4)
219	lfd	fr0, -16(r1)
220	MTMSRD(r6)
221	isync
222	blr
223
224/* Convert single-precision to double, without disturbing FPRs. */
225/* conv_sp_to_dp(double *dp, float *sp) */
226_GLOBAL(conv_dp_to_sp)
227	mfmsr	r6
228	ori	r7, r6, MSR_FP
229	MTMSRD(r7)
230	isync
231	stfd	fr0, -16(r1)
232	lfd	fr0, 0(r3)
233	stfs	fr0, 0(r4)
234	lfd	fr0, -16(r1)
235	MTMSRD(r6)
236	isync
237	blr
238