1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  *  linux/arch/arm/kernel/iwmmxt.S
4  *
5  *  XScale iWMMXt (Concan) context switching and handling
6  *
7  *  Initial code:
8  *  Copyright (c) 2003, Intel Corporation
9  *
10  *  Full lazy switching support, optimizations and more, by Nicolas Pitre
11 *   Copyright (c) 2003-2004, MontaVista Software, Inc.
12  */
13 
14 #include <linux/linkage.h>
15 #include <asm/ptrace.h>
16 #include <asm/thread_info.h>
17 #include <asm/asm-offsets.h>
18 #include <asm/assembler.h>
19 #include "iwmmxt.h"
20 
21 #if defined(CONFIG_CPU_PJ4) || defined(CONFIG_CPU_PJ4B)
22 #define PJ4(code...)		code
23 #define XSC(code...)
24 #elif defined(CONFIG_CPU_MOHAWK) || \
25 	defined(CONFIG_CPU_XSC3) || \
26 	defined(CONFIG_CPU_XSCALE)
27 #define PJ4(code...)
28 #define XSC(code...)		code
29 #else
30 #error "Unsupported iWMMXt architecture"
31 #endif
32 
33 #define MMX_WR0		 	(0x00)
34 #define MMX_WR1		 	(0x08)
35 #define MMX_WR2		 	(0x10)
36 #define MMX_WR3			(0x18)
37 #define MMX_WR4		 	(0x20)
38 #define MMX_WR5		 	(0x28)
39 #define MMX_WR6		 	(0x30)
40 #define MMX_WR7		 	(0x38)
41 #define MMX_WR8		 	(0x40)
42 #define MMX_WR9		 	(0x48)
43 #define MMX_WR10		(0x50)
44 #define MMX_WR11		(0x58)
45 #define MMX_WR12		(0x60)
46 #define MMX_WR13		(0x68)
47 #define MMX_WR14		(0x70)
48 #define MMX_WR15		(0x78)
49 #define MMX_WCSSF		(0x80)
50 #define MMX_WCASF		(0x84)
51 #define MMX_WCGR0		(0x88)
52 #define MMX_WCGR1		(0x8C)
53 #define MMX_WCGR2		(0x90)
54 #define MMX_WCGR3		(0x94)
55 
56 #define MMX_SIZE		(0x98)
57 
58 	.text
59 	.arm
60 
61 /*
62  * Lazy switching of Concan coprocessor context
63  *
64  * r10 = struct thread_info pointer
65  * r9  = ret_from_exception
66  * lr  = undefined instr exit
67  *
68  * called from prefetch exception handler with interrupts enabled
69  */
70 
71 ENTRY(iwmmxt_task_enable)
72 	inc_preempt_count r10, r3
73 
74 	XSC(mrc	p15, 0, r2, c15, c1, 0)
75 	PJ4(mrc p15, 0, r2, c1, c0, 2)
76 	@ CP0 and CP1 accessible?
77 	XSC(tst	r2, #0x3)
78 	PJ4(tst	r2, #0xf)
79 	bne	4f				@ if so no business here
80 	@ enable access to CP0 and CP1
81 	XSC(orr	r2, r2, #0x3)
82 	XSC(mcr	p15, 0, r2, c15, c1, 0)
83 	PJ4(orr	r2, r2, #0xf)
84 	PJ4(mcr	p15, 0, r2, c1, c0, 2)
85 
86 	ldr	r3, =concan_owner
87 	add	r0, r10, #TI_IWMMXT_STATE	@ get task Concan save area
88 	ldr	r2, [sp, #60]			@ current task pc value
89 	ldr	r1, [r3]			@ get current Concan owner
90 	str	r0, [r3]			@ this task now owns Concan regs
91 	sub	r2, r2, #4			@ adjust pc back
92 	str	r2, [sp, #60]
93 
94 	mrc	p15, 0, r2, c2, c0, 0
95 	mov	r2, r2				@ cpwait
96 	bl	concan_save
97 
98 #ifdef CONFIG_PREEMPT_COUNT
99 	get_thread_info r10
100 #endif
101 4:	dec_preempt_count r10, r3
102 	ret	r9				@ normal exit from exception
103 
104 concan_save:
105 
106 	teq	r1, #0				@ test for last ownership
107 	beq	concan_load			@ no owner, skip save
108 
109 	tmrc	r2, wCon
110 
111 	@ CUP? wCx
112 	tst	r2, #0x1
113 	beq 	1f
114 
115 concan_dump:
116 
117 	wstrw	wCSSF, r1, MMX_WCSSF
118 	wstrw	wCASF, r1, MMX_WCASF
119 	wstrw	wCGR0, r1, MMX_WCGR0
120 	wstrw	wCGR1, r1, MMX_WCGR1
121 	wstrw	wCGR2, r1, MMX_WCGR2
122 	wstrw	wCGR3, r1, MMX_WCGR3
123 
124 1:	@ MUP? wRn
125 	tst	r2, #0x2
126 	beq	2f
127 
128 	wstrd	wR0,  r1, MMX_WR0
129 	wstrd	wR1,  r1, MMX_WR1
130 	wstrd	wR2,  r1, MMX_WR2
131 	wstrd	wR3,  r1, MMX_WR3
132 	wstrd	wR4,  r1, MMX_WR4
133 	wstrd	wR5,  r1, MMX_WR5
134 	wstrd	wR6,  r1, MMX_WR6
135 	wstrd	wR7,  r1, MMX_WR7
136 	wstrd	wR8,  r1, MMX_WR8
137 	wstrd	wR9,  r1, MMX_WR9
138 	wstrd	wR10, r1, MMX_WR10
139 	wstrd	wR11, r1, MMX_WR11
140 	wstrd	wR12, r1, MMX_WR12
141 	wstrd	wR13, r1, MMX_WR13
142 	wstrd	wR14, r1, MMX_WR14
143 	wstrd	wR15, r1, MMX_WR15
144 
145 2:	teq	r0, #0				@ anything to load?
146 	reteq	lr				@ if not, return
147 
148 concan_load:
149 
150 	@ Load wRn
151 	wldrd	wR0,  r0, MMX_WR0
152 	wldrd	wR1,  r0, MMX_WR1
153 	wldrd	wR2,  r0, MMX_WR2
154 	wldrd	wR3,  r0, MMX_WR3
155 	wldrd	wR4,  r0, MMX_WR4
156 	wldrd	wR5,  r0, MMX_WR5
157 	wldrd	wR6,  r0, MMX_WR6
158 	wldrd	wR7,  r0, MMX_WR7
159 	wldrd	wR8,  r0, MMX_WR8
160 	wldrd	wR9,  r0, MMX_WR9
161 	wldrd	wR10, r0, MMX_WR10
162 	wldrd	wR11, r0, MMX_WR11
163 	wldrd	wR12, r0, MMX_WR12
164 	wldrd	wR13, r0, MMX_WR13
165 	wldrd	wR14, r0, MMX_WR14
166 	wldrd	wR15, r0, MMX_WR15
167 
168 	@ Load wCx
169 	wldrw	wCSSF, r0, MMX_WCSSF
170 	wldrw	wCASF, r0, MMX_WCASF
171 	wldrw	wCGR0, r0, MMX_WCGR0
172 	wldrw	wCGR1, r0, MMX_WCGR1
173 	wldrw	wCGR2, r0, MMX_WCGR2
174 	wldrw	wCGR3, r0, MMX_WCGR3
175 
176 	@ clear CUP/MUP (only if r1 != 0)
177 	teq	r1, #0
178 	mov 	r2, #0
179 	reteq	lr
180 
181 	tmcr	wCon, r2
182 	ret	lr
183 
184 ENDPROC(iwmmxt_task_enable)
185 
186 /*
187  * Back up Concan regs to save area and disable access to them
188  * (mainly for gdb or sleep mode usage)
189  *
190  * r0 = struct thread_info pointer of target task or NULL for any
191  */
192 
193 ENTRY(iwmmxt_task_disable)
194 
195 	stmfd	sp!, {r4, lr}
196 
197 	mrs	ip, cpsr
198 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
199 	msr	cpsr_c, r2
200 
201 	ldr	r3, =concan_owner
202 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
203 	ldr	r1, [r3]			@ get current Concan owner
204 	teq	r1, #0				@ any current owner?
205 	beq	1f				@ no: quit
206 	teq	r0, #0				@ any owner?
207 	teqne	r1, r2				@ or specified one?
208 	bne	1f				@ no: quit
209 
210 	@ enable access to CP0 and CP1
211 	XSC(mrc	p15, 0, r4, c15, c1, 0)
212 	XSC(orr	r4, r4, #0x3)
213 	XSC(mcr	p15, 0, r4, c15, c1, 0)
214 	PJ4(mrc p15, 0, r4, c1, c0, 2)
215 	PJ4(orr	r4, r4, #0xf)
216 	PJ4(mcr	p15, 0, r4, c1, c0, 2)
217 
218 	mov	r0, #0				@ nothing to load
219 	str	r0, [r3]			@ no more current owner
220 	mrc	p15, 0, r2, c2, c0, 0
221 	mov	r2, r2				@ cpwait
222 	bl	concan_save
223 
224 	@ disable access to CP0 and CP1
225 	XSC(bic	r4, r4, #0x3)
226 	XSC(mcr	p15, 0, r4, c15, c1, 0)
227 	PJ4(bic	r4, r4, #0xf)
228 	PJ4(mcr	p15, 0, r4, c1, c0, 2)
229 
230 	mrc	p15, 0, r2, c2, c0, 0
231 	mov	r2, r2				@ cpwait
232 
233 1:	msr	cpsr_c, ip			@ restore interrupt mode
234 	ldmfd	sp!, {r4, pc}
235 
236 ENDPROC(iwmmxt_task_disable)
237 
238 /*
239  * Copy Concan state to given memory address
240  *
241  * r0 = struct thread_info pointer of target task
242  * r1 = memory address where to store Concan state
243  *
244  * this is called mainly in the creation of signal stack frames
245  */
246 
247 ENTRY(iwmmxt_task_copy)
248 
249 	mrs	ip, cpsr
250 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
251 	msr	cpsr_c, r2
252 
253 	ldr	r3, =concan_owner
254 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
255 	ldr	r3, [r3]			@ get current Concan owner
256 	teq	r2, r3				@ does this task own it...
257 	beq	1f
258 
259 	@ current Concan values are in the task save area
260 	msr	cpsr_c, ip			@ restore interrupt mode
261 	mov	r0, r1
262 	mov	r1, r2
263 	mov	r2, #MMX_SIZE
264 	b	memcpy
265 
266 1:	@ this task owns Concan regs -- grab a copy from there
267 	mov	r0, #0				@ nothing to load
268 	mov	r2, #3				@ save all regs
269 	mov	r3, lr				@ preserve return address
270 	bl	concan_dump
271 	msr	cpsr_c, ip			@ restore interrupt mode
272 	ret	r3
273 
274 ENDPROC(iwmmxt_task_copy)
275 
276 /*
277  * Restore Concan state from given memory address
278  *
279  * r0 = struct thread_info pointer of target task
280  * r1 = memory address where to get Concan state from
281  *
282  * this is used to restore Concan state when unwinding a signal stack frame
283  */
284 
285 ENTRY(iwmmxt_task_restore)
286 
287 	mrs	ip, cpsr
288 	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
289 	msr	cpsr_c, r2
290 
291 	ldr	r3, =concan_owner
292 	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
293 	ldr	r3, [r3]			@ get current Concan owner
294 	bic	r2, r2, #0x7			@ 64-bit alignment
295 	teq	r2, r3				@ does this task own it...
296 	beq	1f
297 
298 	@ this task doesn't own Concan regs -- use its save area
299 	msr	cpsr_c, ip			@ restore interrupt mode
300 	mov	r0, r2
301 	mov	r2, #MMX_SIZE
302 	b	memcpy
303 
304 1:	@ this task owns Concan regs -- load them directly
305 	mov	r0, r1
306 	mov	r1, #0				@ don't clear CUP/MUP
307 	mov	r3, lr				@ preserve return address
308 	bl	concan_load
309 	msr	cpsr_c, ip			@ restore interrupt mode
310 	ret	r3
311 
312 ENDPROC(iwmmxt_task_restore)
313 
314 /*
315  * Concan handling on task switch
316  *
317  * r0 = next thread_info pointer
318  *
319  * Called only from the iwmmxt notifier with task preemption disabled.
320  */
321 ENTRY(iwmmxt_task_switch)
322 
323 	XSC(mrc	p15, 0, r1, c15, c1, 0)
324 	PJ4(mrc	p15, 0, r1, c1, c0, 2)
325 	@ CP0 and CP1 accessible?
326 	XSC(tst	r1, #0x3)
327 	PJ4(tst	r1, #0xf)
328 	bne	1f				@ yes: block them for next task
329 
330 	ldr	r2, =concan_owner
331 	add	r3, r0, #TI_IWMMXT_STATE	@ get next task Concan save area
332 	ldr	r2, [r2]			@ get current Concan owner
333 	teq	r2, r3				@ next task owns it?
334 	retne	lr				@ no: leave Concan disabled
335 
336 1:	@ flip Concan access
337 	XSC(eor	r1, r1, #0x3)
338 	XSC(mcr	p15, 0, r1, c15, c1, 0)
339 	PJ4(eor r1, r1, #0xf)
340 	PJ4(mcr	p15, 0, r1, c1, c0, 2)
341 
342 	mrc	p15, 0, r1, c2, c0, 0
343 	sub	pc, lr, r1, lsr #32		@ cpwait and return
344 
345 ENDPROC(iwmmxt_task_switch)
346 
347 /*
348  * Remove Concan ownership of given task
349  *
350  * r0 = struct thread_info pointer
351  */
352 ENTRY(iwmmxt_task_release)
353 
354 	mrs	r2, cpsr
355 	orr	ip, r2, #PSR_I_BIT		@ disable interrupts
356 	msr	cpsr_c, ip
357 	ldr	r3, =concan_owner
358 	add	r0, r0, #TI_IWMMXT_STATE	@ get task Concan save area
359 	ldr	r1, [r3]			@ get current Concan owner
360 	eors	r0, r0, r1			@ if equal...
361 	streq	r0, [r3]			@ then clear ownership
362 	msr	cpsr_c, r2			@ restore interrupts
363 	ret	lr
364 
365 ENDPROC(iwmmxt_task_release)
366 
367 	.data
368 	.align	2
369 concan_owner:
370 	.word	0
371 
372