GNU Linux-libre 5.4.274-gnu1
[releases.git] / arch / xtensa / kernel / coprocessor.S
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  *
11  * Copyright (C) 2003 - 2007 Tensilica Inc.
12  */
13
14
15 #include <linux/linkage.h>
16 #include <asm/asm-offsets.h>
17 #include <asm/asmmacro.h>
18 #include <asm/processor.h>
19 #include <asm/coprocessor.h>
20 #include <asm/thread_info.h>
21 #include <asm/asm-uaccess.h>
22 #include <asm/unistd.h>
23 #include <asm/ptrace.h>
24 #include <asm/current.h>
25 #include <asm/pgtable.h>
26 #include <asm/page.h>
27 #include <asm/signal.h>
28 #include <asm/tlbflush.h>
29
30 #if XTENSA_HAVE_COPROCESSORS
31
32 /*
33  * Macros for lazy context switch. 
34  */
35
36 #define SAVE_CP_REGS(x)                                                 \
37         .if XTENSA_HAVE_COPROCESSOR(x);                                 \
38                 .align 4;                                               \
39         .Lsave_cp_regs_cp##x:                                           \
40                 xchal_cp##x##_store a2 a3 a4 a5 a6;                     \
41                 jx      a0;                                             \
42         .endif
43
44 #define SAVE_CP_REGS_TAB(x)                                             \
45         .if XTENSA_HAVE_COPROCESSOR(x);                                 \
46                 .long .Lsave_cp_regs_cp##x;                             \
47         .else;                                                          \
48                 .long 0;                                                \
49         .endif;                                                         \
50         .long THREAD_XTREGS_CP##x
51
52
53 #define LOAD_CP_REGS(x)                                                 \
54         .if XTENSA_HAVE_COPROCESSOR(x);                                 \
55                 .align 4;                                               \
56         .Lload_cp_regs_cp##x:                                           \
57                 xchal_cp##x##_load a2 a3 a4 a5 a6;                      \
58                 jx      a0;                                             \
59         .endif
60
61 #define LOAD_CP_REGS_TAB(x)                                             \
62         .if XTENSA_HAVE_COPROCESSOR(x);                                 \
63                 .long .Lload_cp_regs_cp##x;                             \
64         .else;                                                          \
65                 .long 0;                                                \
66         .endif;                                                         \
67         .long THREAD_XTREGS_CP##x
68
69         SAVE_CP_REGS(0)
70         SAVE_CP_REGS(1)
71         SAVE_CP_REGS(2)
72         SAVE_CP_REGS(3)
73         SAVE_CP_REGS(4)
74         SAVE_CP_REGS(5)
75         SAVE_CP_REGS(6)
76         SAVE_CP_REGS(7)
77
78         LOAD_CP_REGS(0)
79         LOAD_CP_REGS(1)
80         LOAD_CP_REGS(2)
81         LOAD_CP_REGS(3)
82         LOAD_CP_REGS(4)
83         LOAD_CP_REGS(5)
84         LOAD_CP_REGS(6)
85         LOAD_CP_REGS(7)
86
87         .section ".rodata", "a"
88         .align 4
89 .Lsave_cp_regs_jump_table:
90         SAVE_CP_REGS_TAB(0)
91         SAVE_CP_REGS_TAB(1)
92         SAVE_CP_REGS_TAB(2)
93         SAVE_CP_REGS_TAB(3)
94         SAVE_CP_REGS_TAB(4)
95         SAVE_CP_REGS_TAB(5)
96         SAVE_CP_REGS_TAB(6)
97         SAVE_CP_REGS_TAB(7)
98
99 .Lload_cp_regs_jump_table:
100         LOAD_CP_REGS_TAB(0)
101         LOAD_CP_REGS_TAB(1)
102         LOAD_CP_REGS_TAB(2)
103         LOAD_CP_REGS_TAB(3)
104         LOAD_CP_REGS_TAB(4)
105         LOAD_CP_REGS_TAB(5)
106         LOAD_CP_REGS_TAB(6)
107         LOAD_CP_REGS_TAB(7)
108
109         .previous
110
111 /*
112  * Entry condition:
113  *
114  *   a0:        trashed, original value saved on stack (PT_AREG0)
115  *   a1:        a1
116  *   a2:        new stack pointer, original in DEPC
117  *   a3:        a3
118  *   depc:      a2, original value saved on stack (PT_DEPC)
119  *   excsave_1: dispatch table
120  *
121  *   PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
122  *           <  VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
123  */
124
125 ENTRY(fast_coprocessor_double)
126
127         wsr     a0, excsave1
128         call0   unrecoverable_exception
129
130 ENDPROC(fast_coprocessor_double)
131
132 ENTRY(fast_coprocessor)
133
134         /* Save remaining registers a1-a3 and SAR */
135
136         s32i    a3, a2, PT_AREG3
137         rsr     a3, sar
138         s32i    a1, a2, PT_AREG1
139         s32i    a3, a2, PT_SAR
140         mov     a1, a2
141         rsr     a2, depc
142         s32i    a2, a1, PT_AREG2
143
144         /*
145          * The hal macros require up to 4 temporary registers. We use a3..a6.
146          */
147
148         s32i    a4, a1, PT_AREG4
149         s32i    a5, a1, PT_AREG5
150         s32i    a6, a1, PT_AREG6
151
152         /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
153
154         rsr     a3, exccause
155         addi    a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
156
157         /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/
158
159         ssl     a3                      # SAR: 32 - coprocessor_number
160         movi    a2, 1
161         rsr     a0, cpenable
162         sll     a2, a2
163         or      a0, a0, a2
164         wsr     a0, cpenable
165         rsync
166
167         /* Retrieve previous owner. (a3 still holds CP number) */
168
169         movi    a0, coprocessor_owner   # list of owners
170         addx4   a0, a3, a0              # entry for CP
171         l32i    a4, a0, 0
172
173         beqz    a4, 1f                  # skip 'save' if no previous owner
174
175         /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
176
177         l32i    a5, a4, THREAD_CPENABLE
178         xor     a5, a5, a2              # (1 << cp-id) still in a2
179         s32i    a5, a4, THREAD_CPENABLE
180
181         /*
182          * Get context save area and 'call' save routine. 
183          * (a4 still holds previous owner (thread_info), a3 CP number)
184          */
185
186         movi    a5, .Lsave_cp_regs_jump_table
187         movi    a0, 2f                  # a0: 'return' address
188         addx8   a3, a3, a5              # a3: coprocessor number
189         l32i    a2, a3, 4               # a2: xtregs offset
190         l32i    a3, a3, 0               # a3: jump address
191         add     a2, a2, a4
192         jx      a3
193
194         /* Note that only a0 and a1 were preserved. */
195
196 2:      rsr     a3, exccause
197         addi    a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
198         movi    a0, coprocessor_owner
199         addx4   a0, a3, a0
200
201         /* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */
202
203 1:      GET_THREAD_INFO (a4, a1)
204         s32i    a4, a0, 0
205
206         /* Get context save area and 'call' load routine. */
207
208         movi    a5, .Lload_cp_regs_jump_table
209         movi    a0, 1f
210         addx8   a3, a3, a5
211         l32i    a2, a3, 4               # a2: xtregs offset
212         l32i    a3, a3, 0               # a3: jump address
213         add     a2, a2, a4
214         jx      a3
215
216         /* Restore all registers and return from exception handler. */
217
218 1:      l32i    a6, a1, PT_AREG6
219         l32i    a5, a1, PT_AREG5
220         l32i    a4, a1, PT_AREG4
221
222         l32i    a0, a1, PT_SAR
223         l32i    a3, a1, PT_AREG3
224         l32i    a2, a1, PT_AREG2
225         wsr     a0, sar
226         l32i    a0, a1, PT_AREG0
227         l32i    a1, a1, PT_AREG1
228
229         rfe
230
231 ENDPROC(fast_coprocessor)
232
233         .text
234
235 /*
236  * coprocessor_flush(struct thread_info*, index)
237  *                             a2        a3
238  *
239  * Save coprocessor registers for coprocessor 'index'.
240  * The register values are saved to or loaded from the coprocessor area
241  * inside the task_info structure.
242  *
243  * Note that this function doesn't update the coprocessor_owner information!
244  *
245  */
246
247 ENTRY(coprocessor_flush)
248
249         /* reserve 4 bytes on stack to save a0 */
250         abi_entry(4)
251
252         s32i    a0, a1, 0
253         movi    a0, .Lsave_cp_regs_jump_table
254         addx8   a3, a3, a0
255         l32i    a4, a3, 4
256         l32i    a3, a3, 0
257         add     a2, a2, a4
258         beqz    a3, 1f
259         callx0  a3
260 1:      l32i    a0, a1, 0
261
262         abi_ret(4)
263
264 ENDPROC(coprocessor_flush)
265
266         .data
267
268 ENTRY(coprocessor_owner)
269
270         .fill XCHAL_CP_MAX, 4, 0
271
272 END(coprocessor_owner)
273
274 #endif /* XTENSA_HAVE_COPROCESSORS */