Commit | Line | Data |
---|---|---|
5a0015d6 CZ |
1 | /* |
2 | * arch/xtensa/kernel/coprocessor.S | |
3 | * | |
4 | * Xtensa processor configuration-specific table of coprocessor and | |
5 | * other custom register layout information. | |
6 | * | |
7 | * This file is subject to the terms and conditions of the GNU General Public | |
8 | * License. See the file "COPYING" in the main directory of this archive | |
9 | * for more details. | |
10 | * | |
c658eac6 | 11 | * Copyright (C) 2003 - 2007 Tensilica Inc. |
5a0015d6 CZ |
12 | */ |
13 | ||
5a0015d6 | 14 | |
5a0015d6 | 15 | #include <linux/linkage.h> |
c658eac6 | 16 | #include <asm/asm-offsets.h> |
5a0015d6 | 17 | #include <asm/processor.h> |
c658eac6 CZ |
18 | #include <asm/coprocessor.h> |
19 | #include <asm/thread_info.h> | |
20 | #include <asm/uaccess.h> | |
21 | #include <asm/unistd.h> | |
22 | #include <asm/ptrace.h> | |
23 | #include <asm/current.h> | |
24 | #include <asm/pgtable.h> | |
25 | #include <asm/page.h> | |
26 | #include <asm/signal.h> | |
27 | #include <asm/tlbflush.h> | |
5a0015d6 | 28 | |
c658eac6 CZ |
29 | /* |
30 | * Entry condition: | |
31 | * | |
32 | * a0: trashed, original value saved on stack (PT_AREG0) | |
33 | * a1: a1 | |
34 | * a2: new stack pointer, original in DEPC | |
99d5040e | 35 | * a3: a3 |
c658eac6 | 36 | * depc: a2, original value saved on stack (PT_DEPC) |
99d5040e | 37 | * excsave_1: dispatch table |
c658eac6 CZ |
38 | * |
39 | * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC | |
40 | * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception | |
41 | */ | |
5a0015d6 | 42 | |
c658eac6 | 43 | /* IO protection is currently unsupported. */ |
5a0015d6 | 44 | |
c658eac6 | 45 | ENTRY(fast_io_protect) |
d1538c46 | 46 | |
bc5378fc | 47 | wsr a0, excsave1 |
c658eac6 CZ |
48 | movi a0, unrecoverable_exception |
49 | callx0 a0 | |
5a0015d6 | 50 | |
d1538c46 CZ |
51 | ENDPROC(fast_io_protect) |
52 | ||
c658eac6 | 53 | #if XTENSA_HAVE_COPROCESSORS |
5a0015d6 | 54 | |
c658eac6 CZ |
55 | /* |
56 | * Macros for lazy context switch. | |
57 | */ | |
5a0015d6 | 58 | |
c658eac6 CZ |
59 | #define SAVE_CP_REGS(x) \ |
60 | .align 4; \ | |
61 | .Lsave_cp_regs_cp##x: \ | |
62 | .if XTENSA_HAVE_COPROCESSOR(x); \ | |
63 | xchal_cp##x##_store a2 a4 a5 a6 a7; \ | |
64 | .endif; \ | |
65 | jx a0 | |
5a0015d6 | 66 | |
c658eac6 CZ |
67 | #define SAVE_CP_REGS_TAB(x) \ |
68 | .if XTENSA_HAVE_COPROCESSOR(x); \ | |
69 | .long .Lsave_cp_regs_cp##x - .Lsave_cp_regs_jump_table; \ | |
70 | .else; \ | |
71 | .long 0; \ | |
72 | .endif; \ | |
73 | .long THREAD_XTREGS_CP##x | |
5a0015d6 | 74 | |
5a0015d6 | 75 | |
c658eac6 CZ |
76 | #define LOAD_CP_REGS(x) \ |
77 | .align 4; \ | |
78 | .Lload_cp_regs_cp##x: \ | |
79 | .if XTENSA_HAVE_COPROCESSOR(x); \ | |
80 | xchal_cp##x##_load a2 a4 a5 a6 a7; \ | |
81 | .endif; \ | |
82 | jx a0 | |
5a0015d6 | 83 | |
c658eac6 CZ |
84 | #define LOAD_CP_REGS_TAB(x) \ |
85 | .if XTENSA_HAVE_COPROCESSOR(x); \ | |
86 | .long .Lload_cp_regs_cp##x - .Lload_cp_regs_jump_table; \ | |
87 | .else; \ | |
88 | .long 0; \ | |
89 | .endif; \ | |
90 | .long THREAD_XTREGS_CP##x | |
5a0015d6 | 91 | |
c658eac6 CZ |
92 | SAVE_CP_REGS(0) |
93 | SAVE_CP_REGS(1) | |
94 | SAVE_CP_REGS(2) | |
95 | SAVE_CP_REGS(3) | |
96 | SAVE_CP_REGS(4) | |
97 | SAVE_CP_REGS(5) | |
98 | SAVE_CP_REGS(6) | |
99 | SAVE_CP_REGS(7) | |
5a0015d6 | 100 | |
c658eac6 CZ |
101 | LOAD_CP_REGS(0) |
102 | LOAD_CP_REGS(1) | |
103 | LOAD_CP_REGS(2) | |
104 | LOAD_CP_REGS(3) | |
105 | LOAD_CP_REGS(4) | |
106 | LOAD_CP_REGS(5) | |
107 | LOAD_CP_REGS(6) | |
108 | LOAD_CP_REGS(7) | |
5a0015d6 | 109 | |
c658eac6 CZ |
110 | .align 4 |
111 | .Lsave_cp_regs_jump_table: | |
112 | SAVE_CP_REGS_TAB(0) | |
113 | SAVE_CP_REGS_TAB(1) | |
114 | SAVE_CP_REGS_TAB(2) | |
115 | SAVE_CP_REGS_TAB(3) | |
116 | SAVE_CP_REGS_TAB(4) | |
117 | SAVE_CP_REGS_TAB(5) | |
118 | SAVE_CP_REGS_TAB(6) | |
119 | SAVE_CP_REGS_TAB(7) | |
5a0015d6 | 120 | |
c658eac6 CZ |
121 | .Lload_cp_regs_jump_table: |
122 | LOAD_CP_REGS_TAB(0) | |
123 | LOAD_CP_REGS_TAB(1) | |
124 | LOAD_CP_REGS_TAB(2) | |
125 | LOAD_CP_REGS_TAB(3) | |
126 | LOAD_CP_REGS_TAB(4) | |
127 | LOAD_CP_REGS_TAB(5) | |
128 | LOAD_CP_REGS_TAB(6) | |
129 | LOAD_CP_REGS_TAB(7) | |
5a0015d6 | 130 | |
c658eac6 CZ |
131 | /* |
132 | * coprocessor_save(buffer, index) | |
133 | * a2 a3 | |
134 | * coprocessor_load(buffer, index) | |
135 | * a2 a3 | |
136 | * | |
137 | * Save or load coprocessor registers for coprocessor 'index'. | |
138 | * The register values are saved to or loaded from them 'buffer' address. | |
139 | * | |
140 | * Note that these functions don't update the coprocessor_owner information! | |
141 | * | |
142 | */ | |
5a0015d6 | 143 | |
c658eac6 | 144 | ENTRY(coprocessor_save) |
d1538c46 | 145 | |
c658eac6 CZ |
146 | entry a1, 32 |
147 | s32i a0, a1, 0 | |
148 | movi a0, .Lsave_cp_regs_jump_table | |
149 | addx8 a3, a3, a0 | |
150 | l32i a3, a3, 0 | |
151 | beqz a3, 1f | |
152 | add a0, a0, a3 | |
153 | callx0 a0 | |
154 | 1: l32i a0, a1, 0 | |
5a0015d6 CZ |
155 | retw |
156 | ||
d1538c46 CZ |
157 | ENDPROC(coprocessor_save) |
158 | ||
c658eac6 | 159 | ENTRY(coprocessor_load) |
d1538c46 | 160 | |
c658eac6 CZ |
161 | entry a1, 32 |
162 | s32i a0, a1, 0 | |
163 | movi a0, .Lload_cp_regs_jump_table | |
164 | addx4 a3, a3, a0 | |
165 | l32i a3, a3, 0 | |
166 | beqz a3, 1f | |
167 | add a0, a0, a3 | |
168 | callx0 a0 | |
169 | 1: l32i a0, a1, 0 | |
5a0015d6 CZ |
170 | retw |
171 | ||
d1538c46 CZ |
172 | ENDPROC(coprocessor_load) |
173 | ||
c658eac6 | 174 | /* |
c4c4594b | 175 | * coprocessor_flush(struct task_info*, index) |
c658eac6 CZ |
176 | * a2 a3 |
177 | * coprocessor_restore(struct task_info*, index) | |
178 | * a2 a3 | |
179 | * | |
180 | * Save or load coprocessor registers for coprocessor 'index'. | |
181 | * The register values are saved to or loaded from the coprocessor area | |
182 | * inside the task_info structure. | |
183 | * | |
184 | * Note that these functions don't update the coprocessor_owner information! | |
185 | * | |
186 | */ | |
187 | ||
188 | ||
189 | ENTRY(coprocessor_flush) | |
d1538c46 | 190 | |
c658eac6 CZ |
191 | entry a1, 32 |
192 | s32i a0, a1, 0 | |
193 | movi a0, .Lsave_cp_regs_jump_table | |
194 | addx8 a3, a3, a0 | |
195 | l32i a4, a3, 4 | |
196 | l32i a3, a3, 0 | |
197 | add a2, a2, a4 | |
198 | beqz a3, 1f | |
199 | add a0, a0, a3 | |
200 | callx0 a0 | |
201 | 1: l32i a0, a1, 0 | |
5a0015d6 CZ |
202 | retw |
203 | ||
d1538c46 CZ |
204 | ENDPROC(coprocessor_flush) |
205 | ||
c658eac6 CZ |
206 | ENTRY(coprocessor_restore) |
207 | entry a1, 32 | |
208 | s32i a0, a1, 0 | |
209 | movi a0, .Lload_cp_regs_jump_table | |
210 | addx4 a3, a3, a0 | |
211 | l32i a4, a3, 4 | |
212 | l32i a3, a3, 0 | |
213 | add a2, a2, a4 | |
214 | beqz a3, 1f | |
215 | add a0, a0, a3 | |
216 | callx0 a0 | |
217 | 1: l32i a0, a1, 0 | |
218 | retw | |
5a0015d6 | 219 | |
d1538c46 CZ |
220 | ENDPROC(coprocessor_restore) |
221 | ||
5a0015d6 | 222 | /* |
c658eac6 | 223 | * Entry condition: |
5a0015d6 | 224 | * |
c658eac6 CZ |
225 | * a0: trashed, original value saved on stack (PT_AREG0) |
226 | * a1: a1 | |
227 | * a2: new stack pointer, original in DEPC | |
99d5040e | 228 | * a3: a3 |
c658eac6 | 229 | * depc: a2, original value saved on stack (PT_DEPC) |
99d5040e | 230 | * excsave_1: dispatch table |
5a0015d6 | 231 | * |
c658eac6 CZ |
232 | * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC |
233 | * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception | |
5a0015d6 CZ |
234 | */ |
235 | ||
c658eac6 | 236 | ENTRY(fast_coprocessor_double) |
d1538c46 | 237 | |
bc5378fc | 238 | wsr a0, excsave1 |
c658eac6 CZ |
239 | movi a0, unrecoverable_exception |
240 | callx0 a0 | |
5a0015d6 | 241 | |
d1538c46 | 242 | ENDPROC(fast_coprocessor_double) |
5a0015d6 | 243 | |
c658eac6 CZ |
244 | ENTRY(fast_coprocessor) |
245 | ||
246 | /* Save remaining registers a1-a3 and SAR */ | |
247 | ||
c658eac6 | 248 | s32i a3, a2, PT_AREG3 |
bc5378fc | 249 | rsr a3, sar |
c658eac6 CZ |
250 | s32i a1, a2, PT_AREG1 |
251 | s32i a3, a2, PT_SAR | |
252 | mov a1, a2 | |
bc5378fc | 253 | rsr a2, depc |
c658eac6 CZ |
254 | s32i a2, a1, PT_AREG2 |
255 | ||
256 | /* | |
257 | * The hal macros require up to 4 temporary registers. We use a3..a6. | |
258 | */ | |
259 | ||
260 | s32i a4, a1, PT_AREG4 | |
261 | s32i a5, a1, PT_AREG5 | |
262 | s32i a6, a1, PT_AREG6 | |
263 | ||
264 | /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */ | |
265 | ||
bc5378fc | 266 | rsr a3, exccause |
c658eac6 CZ |
267 | addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED |
268 | ||
269 | /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/ | |
270 | ||
271 | ssl a3 # SAR: 32 - coprocessor_number | |
272 | movi a2, 1 | |
bc5378fc | 273 | rsr a0, cpenable |
c658eac6 CZ |
274 | sll a2, a2 |
275 | or a0, a0, a2 | |
bc5378fc | 276 | wsr a0, cpenable |
c658eac6 CZ |
277 | rsync |
278 | ||
279 | /* Retrieve previous owner. (a3 still holds CP number) */ | |
280 | ||
281 | movi a0, coprocessor_owner # list of owners | |
282 | addx4 a0, a3, a0 # entry for CP | |
283 | l32i a4, a0, 0 | |
284 | ||
285 | beqz a4, 1f # skip 'save' if no previous owner | |
286 | ||
287 | /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */ | |
288 | ||
289 | l32i a5, a4, THREAD_CPENABLE | |
290 | xor a5, a5, a2 # (1 << cp-id) still in a2 | |
291 | s32i a5, a4, THREAD_CPENABLE | |
292 | ||
293 | /* | |
294 | * Get context save area and 'call' save routine. | |
295 | * (a4 still holds previous owner (thread_info), a3 CP number) | |
296 | */ | |
297 | ||
298 | movi a5, .Lsave_cp_regs_jump_table | |
299 | movi a0, 2f # a0: 'return' address | |
300 | addx8 a3, a3, a5 # a3: coprocessor number | |
301 | l32i a2, a3, 4 # a2: xtregs offset | |
302 | l32i a3, a3, 0 # a3: jump offset | |
303 | add a2, a2, a4 | |
304 | add a4, a3, a5 # a4: address of save routine | |
305 | jx a4 | |
306 | ||
307 | /* Note that only a0 and a1 were preserved. */ | |
308 | ||
bc5378fc | 309 | 2: rsr a3, exccause |
c658eac6 CZ |
310 | addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED |
311 | movi a0, coprocessor_owner | |
312 | addx4 a0, a3, a0 | |
313 | ||
314 | /* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */ | |
315 | ||
316 | 1: GET_THREAD_INFO (a4, a1) | |
317 | s32i a4, a0, 0 | |
318 | ||
319 | /* Get context save area and 'call' load routine. */ | |
320 | ||
321 | movi a5, .Lload_cp_regs_jump_table | |
322 | movi a0, 1f | |
323 | addx8 a3, a3, a5 | |
324 | l32i a2, a3, 4 # a2: xtregs offset | |
325 | l32i a3, a3, 0 # a3: jump offset | |
326 | add a2, a2, a4 | |
327 | add a4, a3, a5 | |
328 | jx a4 | |
329 | ||
330 | /* Restore all registers and return from exception handler. */ | |
331 | ||
332 | 1: l32i a6, a1, PT_AREG6 | |
333 | l32i a5, a1, PT_AREG5 | |
334 | l32i a4, a1, PT_AREG4 | |
335 | ||
336 | l32i a0, a1, PT_SAR | |
337 | l32i a3, a1, PT_AREG3 | |
338 | l32i a2, a1, PT_AREG2 | |
bc5378fc | 339 | wsr a0, sar |
c658eac6 CZ |
340 | l32i a0, a1, PT_AREG0 |
341 | l32i a1, a1, PT_AREG1 | |
342 | ||
343 | rfe | |
344 | ||
d1538c46 CZ |
345 | ENDPROC(fast_coprocessor) |
346 | ||
c658eac6 | 347 | .data |
d1538c46 | 348 | |
c658eac6 | 349 | ENTRY(coprocessor_owner) |
d1538c46 | 350 | |
c658eac6 CZ |
351 | .fill XCHAL_CP_MAX, 4, 0 |
352 | ||
d1538c46 CZ |
353 | END(coprocessor_owner) |
354 | ||
c658eac6 | 355 | #endif /* XTENSA_HAVE_COPROCESSORS */ |