summaryrefslogtreecommitdiff
path: root/arch/arm/kernel/iwmmxt.S
blob: 4a335d3c59690bdc37ce14629751e08afdb41806 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/kernel/iwmmxt.S
 *
 *  XScale iWMMXt (Concan) context switching and handling
 *
 *  Initial code:
 *  Copyright (c) 2003, Intel Corporation
 *
 *  Full lazy switching support, optimizations and more, by Nicolas Pitre
*   Copyright (c) 2003-2004, MontaVista Software, Inc.
 */

#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/assembler.h>
#include "iwmmxt.h"

#define MMX_WR0		 	(0x00)
#define MMX_WR1		 	(0x08)
#define MMX_WR2		 	(0x10)
#define MMX_WR3			(0x18)
#define MMX_WR4		 	(0x20)
#define MMX_WR5		 	(0x28)
#define MMX_WR6		 	(0x30)
#define MMX_WR7		 	(0x38)
#define MMX_WR8		 	(0x40)
#define MMX_WR9		 	(0x48)
#define MMX_WR10		(0x50)
#define MMX_WR11		(0x58)
#define MMX_WR12		(0x60)
#define MMX_WR13		(0x68)
#define MMX_WR14		(0x70)
#define MMX_WR15		(0x78)
#define MMX_WCSSF		(0x80)
#define MMX_WCASF		(0x84)
#define MMX_WCGR0		(0x88)
#define MMX_WCGR1		(0x8C)
#define MMX_WCGR2		(0x90)
#define MMX_WCGR3		(0x94)

#define MMX_SIZE		(0x98)

	.text
	.arm

ENTRY(iwmmxt_undef_handler)
	push		{r9, r10, lr}
	get_thread_info	r10
	mov		r9, pc
	b		iwmmxt_task_enable
	mov		r0, #0
	pop		{r9, r10, pc}
ENDPROC(iwmmxt_undef_handler)

/*
 * Lazy switching of Concan coprocessor context
 *
 * r0  = struct pt_regs pointer
 * r10 = struct thread_info pointer
 * r9  = ret_from_exception
 * lr  = undefined instr exit
 *
 * called from prefetch exception handler with interrupts enabled
 */

ENTRY(iwmmxt_task_enable)
	inc_preempt_count r10, r3

	mrc	p15, 0, r2, c15, c1, 0
	@ CP0 and CP1 accessible?
	tst	r2, #0x3
	bne	4f				@ if so no business here
	@ enable access to CP0 and CP1
	orr	r2, r2, #0x3
	mcr	p15, 0, r2, c15, c1, 0

	ldr	r3, =concan_owner
	ldr	r2, [r0, #S_PC]			@ current task pc value
	ldr	r1, [r3]			@ get current Concan owner
	sub	r2, r2, #4			@ adjust pc back
	str	r2, [r0, #S_PC]
	add	r0, r10, #TI_IWMMXT_STATE	@ get task Concan save area
	str	r0, [r3]			@ this task now owns Concan regs

	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait
	bl	concan_save

#ifdef CONFIG_PREEMPT_COUNT
	get_thread_info r10
#endif
4:	dec_preempt_count r10, r3
	ret	r9				@ normal exit from exception

concan_save:

	teq	r1, #0				@ test for last ownership
	beq	concan_load			@ no owner, skip save

	tmrc	r2, wCon

	@ CUP? wCx
	tst	r2, #0x1
	beq 	1f

concan_dump:

	wstrw	wCSSF, r1, MMX_WCSSF
	wstrw	wCASF, r1, MMX_WCASF
	wstrw	wCGR0, r1, MMX_WCGR0
	wstrw	wCGR1, r1, MMX_WCGR1
	wstrw	wCGR2, r1, MMX_WCGR2
	wstrw	wCGR3, r1, MMX_WCGR3

1:	@ MUP? wRn
	tst	r2, #0x2
	beq	2f

	wstrd	wR0,  r1, MMX_WR0
	wstrd	wR1,  r1, MMX_WR1
	wstrd	wR2,  r1, MMX_WR2
	wstrd	wR3,  r1, MMX_WR3
	wstrd	wR4,  r1, MMX_WR4
	wstrd	wR5,  r1, MMX_WR5
	wstrd	wR6,  r1, MMX_WR6
	wstrd	wR7,  r1, MMX_WR7
	wstrd	wR8,  r1, MMX_WR8
	wstrd	wR9,  r1, MMX_WR9
	wstrd	wR10, r1, MMX_WR10
	wstrd	wR11, r1, MMX_WR11
	wstrd	wR12, r1, MMX_WR12
	wstrd	wR13, r1, MMX_WR13
	wstrd	wR14, r1, MMX_WR14
	wstrd	wR15, r1, MMX_WR15

2:	teq	r0, #0				@ anything to load?
	reteq	lr				@ if not, return

concan_load:

	@ Load wRn
	wldrd	wR0,  r0, MMX_WR0
	wldrd	wR1,  r0, MMX_WR1
	wldrd	wR2,  r0, MMX_WR2
	wldrd	wR3,  r0, MMX_WR3
	wldrd	wR4,  r0, MMX_WR4
	wldrd	wR5,  r0, MMX_WR5
	wldrd	wR6,  r0, MMX_WR6
	wldrd	wR7,  r0, MMX_WR7
	wldrd	wR8,  r0, MMX_WR8
	wldrd	wR9,  r0, MMX_WR9
	wldrd	wR10, r0, MMX_WR10
	wldrd	wR11, r0, MMX_WR11
	wldrd	wR12, r0, MMX_WR12
	wldrd	wR13, r0, MMX_WR13
	wldrd	wR14, r0, MMX_WR14
	wldrd	wR15, r0, MMX_WR15

	@ Load wCx
	wldrw	wCSSF, r0, MMX_WCSSF
	wldrw	wCASF, r0, MMX_WCASF
	wldrw	wCGR0, r0, MMX_WCGR0
	wldrw	wCGR1, r0, MMX_WCGR1
	wldrw	wCGR2, r0, MMX_WCGR2
	wldrw	wCGR3, r0, MMX_WCGR3

	@ clear CUP/MUP (only if r1 != 0)
	teq	r1, #0
	mov 	r2, #0
	reteq	lr

	tmcr	wCon, r2
	ret	lr

ENDPROC(iwmmxt_task_enable)

/*
 * Back up Concan regs to save area and disable access to them
 * (mainly for gdb or sleep mode usage)
 *
 * r0 = struct thread_info pointer of target task or NULL for any
 */

ENTRY(iwmmxt_task_disable)

	stmfd	sp!, {r4, lr}

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r1, [r3]			@ get current Concan owner
	teq	r1, #0				@ any current owner?
	beq	1f				@ no: quit
	teq	r0, #0				@ any owner?
	teqne	r1, r2				@ or specified one?
	bne	1f				@ no: quit

	@ enable access to CP0 and CP1
	mrc	p15, 0, r4, c15, c1, 0
	orr	r4, r4, #0x3
	mcr	p15, 0, r4, c15, c1, 0

	mov	r0, #0				@ nothing to load
	str	r0, [r3]			@ no more current owner
	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait
	bl	concan_save

	@ disable access to CP0 and CP1
	bic	r4, r4, #0x3
	mcr	p15, 0, r4, c15, c1, 0

	mrc	p15, 0, r2, c2, c0, 0
	mov	r2, r2				@ cpwait

1:	msr	cpsr_c, ip			@ restore interrupt mode
	ldmfd	sp!, {r4, pc}

ENDPROC(iwmmxt_task_disable)

/*
 * Copy Concan state to given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to store Concan state
 *
 * this is called mainly in the creation of signal stack frames
 */

ENTRY(iwmmxt_task_copy)

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r3, [r3]			@ get current Concan owner
	teq	r2, r3				@ does this task own it...
	beq	1f

	@ current Concan values are in the task save area
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	r0, r1
	mov	r1, r2
	mov	r2, #MMX_SIZE
	b	memcpy

1:	@ this task owns Concan regs -- grab a copy from there
	mov	r0, #0				@ nothing to load
	mov	r2, #3				@ save all regs
	mov	r3, lr				@ preserve return address
	bl	concan_dump
	msr	cpsr_c, ip			@ restore interrupt mode
	ret	r3

ENDPROC(iwmmxt_task_copy)

/*
 * Restore Concan state from given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to get Concan state from
 *
 * this is used to restore Concan state when unwinding a signal stack frame
 */

ENTRY(iwmmxt_task_restore)

	mrs	ip, cpsr
	orr	r2, ip, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, r2

	ldr	r3, =concan_owner
	add	r2, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r3, [r3]			@ get current Concan owner
	bic	r2, r2, #0x7			@ 64-bit alignment
	teq	r2, r3				@ does this task own it...
	beq	1f

	@ this task doesn't own Concan regs -- use its save area
	msr	cpsr_c, ip			@ restore interrupt mode
	mov	r0, r2
	mov	r2, #MMX_SIZE
	b	memcpy

1:	@ this task owns Concan regs -- load them directly
	mov	r0, r1
	mov	r1, #0				@ don't clear CUP/MUP
	mov	r3, lr				@ preserve return address
	bl	concan_load
	msr	cpsr_c, ip			@ restore interrupt mode
	ret	r3

ENDPROC(iwmmxt_task_restore)

/*
 * Concan handling on task switch
 *
 * r0 = next thread_info pointer
 *
 * Called only from the iwmmxt notifier with task preemption disabled.
 */
ENTRY(iwmmxt_task_switch)

	mrc	p15, 0, r1, c15, c1, 0
	@ CP0 and CP1 accessible?
	tst	r1, #0x3
	bne	1f				@ yes: block them for next task

	ldr	r2, =concan_owner
	add	r3, r0, #TI_IWMMXT_STATE	@ get next task Concan save area
	ldr	r2, [r2]			@ get current Concan owner
	teq	r2, r3				@ next task owns it?
	retne	lr				@ no: leave Concan disabled

1:	@ flip Concan access
	eor	r1, r1, #0x3
	mcr	p15, 0, r1, c15, c1, 0

	mrc	p15, 0, r1, c2, c0, 0
	sub	pc, lr, r1, lsr #32		@ cpwait and return

ENDPROC(iwmmxt_task_switch)

/*
 * Remove Concan ownership of given task
 *
 * r0 = struct thread_info pointer
 */
ENTRY(iwmmxt_task_release)

	mrs	r2, cpsr
	orr	ip, r2, #PSR_I_BIT		@ disable interrupts
	msr	cpsr_c, ip
	ldr	r3, =concan_owner
	add	r0, r0, #TI_IWMMXT_STATE	@ get task Concan save area
	ldr	r1, [r3]			@ get current Concan owner
	eors	r0, r0, r1			@ if equal...
	streq	r0, [r3]			@ then clear ownership
	msr	cpsr_c, r2			@ restore interrupts
	ret	lr

ENDPROC(iwmmxt_task_release)

	.data
	.align	2
concan_owner:
	.word	0