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