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