diff options
Diffstat (limited to 'arch/arm/kernel/iwmmxt.S')
-rw-r--r-- | arch/arm/kernel/iwmmxt.S | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/arch/arm/kernel/iwmmxt.S b/arch/arm/kernel/iwmmxt.S new file mode 100644 index 000000000000..8f74e24536ba --- /dev/null +++ b/arch/arm/kernel/iwmmxt.S | |||
@@ -0,0 +1,320 @@ | |||
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 | |||