1/*
2 * arch/arm/kernel/crunch-bits.S
3 * Cirrus MaverickCrunch context switching and handling
4 *
5 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
6 *
7 * Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is
8 * Copyright (c) 2003-2004, MontaVista Software, Inc.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/linkage.h>
16#include <asm/ptrace.h>
17#include <asm/thread_info.h>
18#include <asm/asm-offsets.h>
19#include <asm/assembler.h>
20#include <mach/ep93xx-regs.h>
21
22/*
23 * We can't use hex constants here due to a bug in gas.
24 */
25#define CRUNCH_MVDX0		0
26#define CRUNCH_MVDX1		8
27#define CRUNCH_MVDX2		16
28#define CRUNCH_MVDX3		24
29#define CRUNCH_MVDX4		32
30#define CRUNCH_MVDX5		40
31#define CRUNCH_MVDX6		48
32#define CRUNCH_MVDX7		56
33#define CRUNCH_MVDX8		64
34#define CRUNCH_MVDX9		72
35#define CRUNCH_MVDX10		80
36#define CRUNCH_MVDX11		88
37#define CRUNCH_MVDX12		96
38#define CRUNCH_MVDX13		104
39#define CRUNCH_MVDX14		112
40#define CRUNCH_MVDX15		120
41#define CRUNCH_MVAX0L		128
42#define CRUNCH_MVAX0M		132
43#define CRUNCH_MVAX0H		136
44#define CRUNCH_MVAX1L		140
45#define CRUNCH_MVAX1M		144
46#define CRUNCH_MVAX1H		148
47#define CRUNCH_MVAX2L		152
48#define CRUNCH_MVAX2M		156
49#define CRUNCH_MVAX2H		160
50#define CRUNCH_MVAX3L		164
51#define CRUNCH_MVAX3M		168
52#define CRUNCH_MVAX3H		172
53#define CRUNCH_DSPSC		176
54
55#define CRUNCH_SIZE		184
56
57	.text
58
59/*
60 * Lazy switching of crunch coprocessor context
61 *
62 * r10 = struct thread_info pointer
63 * r9  = ret_from_exception
64 * lr  = undefined instr exit
65 *
66 * called from prefetch exception handler with interrupts enabled
67 */
68ENTRY(crunch_task_enable)
69	inc_preempt_count r10, r3
70
71	ldr	r8, =(EP93XX_APB_VIRT_BASE + 0x00130000)	@ syscon addr
72
73	ldr	r1, [r8, #0x80]
74	tst	r1, #0x00800000			@ access to crunch enabled?
75	bne	2f				@ if so no business here
76	mov	r3, #0xaa			@ unlock syscon swlock
77	str	r3, [r8, #0xc0]
78	orr	r1, r1, #0x00800000		@ enable access to crunch
79	str	r1, [r8, #0x80]
80
81	ldr	r3, =crunch_owner
82	add	r0, r10, #TI_CRUNCH_STATE	@ get task crunch save area
83	ldr	r2, [sp, #60]			@ current task pc value
84	ldr	r1, [r3]			@ get current crunch owner
85	str	r0, [r3]			@ this task now owns crunch
86	sub	r2, r2, #4			@ adjust pc back
87	str	r2, [sp, #60]
88
89	ldr	r2, [r8, #0x80]
90	mov	r2, r2				@ flush out enable (@@@)
91
92	teq	r1, #0				@ test for last ownership
93	mov	lr, r9				@ normal exit from exception
94	beq	crunch_load			@ no owner, skip save
95
96crunch_save:
97	cfstr64		mvdx0, [r1, #CRUNCH_MVDX0]	@ save 64b registers
98	cfstr64		mvdx1, [r1, #CRUNCH_MVDX1]
99	cfstr64		mvdx2, [r1, #CRUNCH_MVDX2]
100	cfstr64		mvdx3, [r1, #CRUNCH_MVDX3]
101	cfstr64		mvdx4, [r1, #CRUNCH_MVDX4]
102	cfstr64		mvdx5, [r1, #CRUNCH_MVDX5]
103	cfstr64		mvdx6, [r1, #CRUNCH_MVDX6]
104	cfstr64		mvdx7, [r1, #CRUNCH_MVDX7]
105	cfstr64		mvdx8, [r1, #CRUNCH_MVDX8]
106	cfstr64		mvdx9, [r1, #CRUNCH_MVDX9]
107	cfstr64		mvdx10, [r1, #CRUNCH_MVDX10]
108	cfstr64		mvdx11, [r1, #CRUNCH_MVDX11]
109	cfstr64		mvdx12, [r1, #CRUNCH_MVDX12]
110	cfstr64		mvdx13, [r1, #CRUNCH_MVDX13]
111	cfstr64		mvdx14, [r1, #CRUNCH_MVDX14]
112	cfstr64		mvdx15, [r1, #CRUNCH_MVDX15]
113
114#ifdef __ARMEB__
115#error fix me for ARMEB
116#endif
117
118	cfmv32al	mvfx0, mvax0			@ save 72b accumulators
119	cfstr32		mvfx0, [r1, #CRUNCH_MVAX0L]
120	cfmv32am	mvfx0, mvax0
121	cfstr32		mvfx0, [r1, #CRUNCH_MVAX0M]
122	cfmv32ah	mvfx0, mvax0
123	cfstr32		mvfx0, [r1, #CRUNCH_MVAX0H]
124	cfmv32al	mvfx0, mvax1
125	cfstr32		mvfx0, [r1, #CRUNCH_MVAX1L]
126	cfmv32am	mvfx0, mvax1
127	cfstr32		mvfx0, [r1, #CRUNCH_MVAX1M]
128	cfmv32ah	mvfx0, mvax1
129	cfstr32		mvfx0, [r1, #CRUNCH_MVAX1H]
130	cfmv32al	mvfx0, mvax2
131	cfstr32		mvfx0, [r1, #CRUNCH_MVAX2L]
132	cfmv32am	mvfx0, mvax2
133	cfstr32		mvfx0, [r1, #CRUNCH_MVAX2M]
134	cfmv32ah	mvfx0, mvax2
135	cfstr32		mvfx0, [r1, #CRUNCH_MVAX2H]
136	cfmv32al	mvfx0, mvax3
137	cfstr32		mvfx0, [r1, #CRUNCH_MVAX3L]
138	cfmv32am	mvfx0, mvax3
139	cfstr32		mvfx0, [r1, #CRUNCH_MVAX3M]
140	cfmv32ah	mvfx0, mvax3
141	cfstr32		mvfx0, [r1, #CRUNCH_MVAX3H]
142
143	cfmv32sc	mvdx0, dspsc			@ save status word
144	cfstr64		mvdx0, [r1, #CRUNCH_DSPSC]
145
146	teq		r0, #0				@ anything to load?
147	cfldr64eq	mvdx0, [r1, #CRUNCH_MVDX0]	@ mvdx0 was clobbered
148	beq		1f
149
150crunch_load:
151	cfldr64		mvdx0, [r0, #CRUNCH_DSPSC]	@ load status word
152	cfmvsc32	dspsc, mvdx0
153
154	cfldr32		mvfx0, [r0, #CRUNCH_MVAX0L]	@ load 72b accumulators
155	cfmval32	mvax0, mvfx0
156	cfldr32		mvfx0, [r0, #CRUNCH_MVAX0M]
157	cfmvam32	mvax0, mvfx0
158	cfldr32		mvfx0, [r0, #CRUNCH_MVAX0H]
159	cfmvah32	mvax0, mvfx0
160	cfldr32		mvfx0, [r0, #CRUNCH_MVAX1L]
161	cfmval32	mvax1, mvfx0
162	cfldr32		mvfx0, [r0, #CRUNCH_MVAX1M]
163	cfmvam32	mvax1, mvfx0
164	cfldr32		mvfx0, [r0, #CRUNCH_MVAX1H]
165	cfmvah32	mvax1, mvfx0
166	cfldr32		mvfx0, [r0, #CRUNCH_MVAX2L]
167	cfmval32	mvax2, mvfx0
168	cfldr32		mvfx0, [r0, #CRUNCH_MVAX2M]
169	cfmvam32	mvax2, mvfx0
170	cfldr32		mvfx0, [r0, #CRUNCH_MVAX2H]
171	cfmvah32	mvax2, mvfx0
172	cfldr32		mvfx0, [r0, #CRUNCH_MVAX3L]
173	cfmval32	mvax3, mvfx0
174	cfldr32		mvfx0, [r0, #CRUNCH_MVAX3M]
175	cfmvam32	mvax3, mvfx0
176	cfldr32		mvfx0, [r0, #CRUNCH_MVAX3H]
177	cfmvah32	mvax3, mvfx0
178
179	cfldr64		mvdx0, [r0, #CRUNCH_MVDX0]	@ load 64b registers
180	cfldr64		mvdx1, [r0, #CRUNCH_MVDX1]
181	cfldr64		mvdx2, [r0, #CRUNCH_MVDX2]
182	cfldr64		mvdx3, [r0, #CRUNCH_MVDX3]
183	cfldr64		mvdx4, [r0, #CRUNCH_MVDX4]
184	cfldr64		mvdx5, [r0, #CRUNCH_MVDX5]
185	cfldr64		mvdx6, [r0, #CRUNCH_MVDX6]
186	cfldr64		mvdx7, [r0, #CRUNCH_MVDX7]
187	cfldr64		mvdx8, [r0, #CRUNCH_MVDX8]
188	cfldr64		mvdx9, [r0, #CRUNCH_MVDX9]
189	cfldr64		mvdx10, [r0, #CRUNCH_MVDX10]
190	cfldr64		mvdx11, [r0, #CRUNCH_MVDX11]
191	cfldr64		mvdx12, [r0, #CRUNCH_MVDX12]
192	cfldr64		mvdx13, [r0, #CRUNCH_MVDX13]
193	cfldr64		mvdx14, [r0, #CRUNCH_MVDX14]
194	cfldr64		mvdx15, [r0, #CRUNCH_MVDX15]
195
1961:
197#ifdef CONFIG_PREEMPT_COUNT
198	get_thread_info r10
199#endif
2002:	dec_preempt_count r10, r3
201	ret	lr
202
203/*
204 * Back up crunch regs to save area and disable access to them
205 * (mainly for gdb or sleep mode usage)
206 *
207 * r0 = struct thread_info pointer of target task or NULL for any
208 */
209ENTRY(crunch_task_disable)
210	stmfd	sp!, {r4, r5, lr}
211
212	mrs	ip, cpsr
213	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
214	msr	cpsr_c, r2
215
216	ldr	r4, =(EP93XX_APB_VIRT_BASE + 0x00130000)	@ syscon addr
217
218	ldr	r3, =crunch_owner
219	add	r2, r0, #TI_CRUNCH_STATE	@ get task crunch save area
220	ldr	r1, [r3]			@ get current crunch owner
221	teq	r1, #0				@ any current owner?
222	beq	1f				@ no: quit
223	teq	r0, #0				@ any owner?
224	teqne	r1, r2				@ or specified one?
225	bne	1f				@ no: quit
226
227	ldr	r5, [r4, #0x80]			@ enable access to crunch
228	mov	r2, #0xaa
229	str	r2, [r4, #0xc0]
230	orr	r5, r5, #0x00800000
231	str	r5, [r4, #0x80]
232
233	mov	r0, #0				@ nothing to load
234	str	r0, [r3]			@ no more current owner
235	ldr	r2, [r4, #0x80]			@ flush out enable (@@@)
236	mov	r2, r2
237	bl	crunch_save
238
239	mov	r2, #0xaa			@ disable access to crunch
240	str	r2, [r4, #0xc0]
241	bic	r5, r5, #0x00800000
242	str	r5, [r4, #0x80]
243	ldr	r5, [r4, #0x80]			@ flush out enable (@@@)
244	mov	r5, r5
245
2461:	msr	cpsr_c, ip			@ restore interrupt mode
247	ldmfd	sp!, {r4, r5, pc}
248
249/*
250 * Copy crunch state to given memory address
251 *
252 * r0 = struct thread_info pointer of target task
253 * r1 = memory address where to store crunch state
254 *
255 * this is called mainly in the creation of signal stack frames
256 */
257ENTRY(crunch_task_copy)
258	mrs	ip, cpsr
259	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
260	msr	cpsr_c, r2
261
262	ldr	r3, =crunch_owner
263	add	r2, r0, #TI_CRUNCH_STATE	@ get task crunch save area
264	ldr	r3, [r3]			@ get current crunch owner
265	teq	r2, r3				@ does this task own it...
266	beq	1f
267
268	@ current crunch values are in the task save area
269	msr	cpsr_c, ip			@ restore interrupt mode
270	mov	r0, r1
271	mov	r1, r2
272	mov	r2, #CRUNCH_SIZE
273	b	memcpy
274
2751:	@ this task owns crunch regs -- grab a copy from there
276	mov	r0, #0				@ nothing to load
277	mov	r3, lr				@ preserve return address
278	bl	crunch_save
279	msr	cpsr_c, ip			@ restore interrupt mode
280	ret	r3
281
282/*
283 * Restore crunch state from given memory address
284 *
285 * r0 = struct thread_info pointer of target task
286 * r1 = memory address where to get crunch state from
287 *
288 * this is used to restore crunch state when unwinding a signal stack frame
289 */
290ENTRY(crunch_task_restore)
291	mrs	ip, cpsr
292	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
293	msr	cpsr_c, r2
294
295	ldr	r3, =crunch_owner
296	add	r2, r0, #TI_CRUNCH_STATE	@ get task crunch save area
297	ldr	r3, [r3]			@ get current crunch owner
298	teq	r2, r3				@ does this task own it...
299	beq	1f
300
301	@ this task doesn't own crunch regs -- use its save area
302	msr	cpsr_c, ip			@ restore interrupt mode
303	mov	r0, r2
304	mov	r2, #CRUNCH_SIZE
305	b	memcpy
306
3071:	@ this task owns crunch regs -- load them directly
308	mov	r0, r1
309	mov	r1, #0				@ nothing to save
310	mov	r3, lr				@ preserve return address
311	bl	crunch_load
312	msr	cpsr_c, ip			@ restore interrupt mode
313	ret	r3
314