Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/xtensa/kernel/coprocessor.S
26439 views
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/coprocessor.h>
19
#include <asm/current.h>
20
#include <asm/regs.h>
21
22
/*
23
* Rules for coprocessor state manipulation on SMP:
24
*
25
* - a task may have live coprocessors only on one CPU.
26
*
27
* - whether coprocessor context of task T is live on some CPU is
28
* denoted by T's thread_info->cpenable.
29
*
30
* - non-zero thread_info->cpenable means that thread_info->cp_owner_cpu
31
* is valid in the T's thread_info. Zero thread_info->cpenable means that
32
* coprocessor context is valid in the T's thread_info.
33
*
34
* - if a coprocessor context of task T is live on CPU X, only CPU X changes
35
* T's thread_info->cpenable, cp_owner_cpu and coprocessor save area.
36
* This is done by making sure that for the task T with live coprocessor
37
* on CPU X cpenable SR is 0 when T runs on any other CPU Y.
38
* When fast_coprocessor exception is taken on CPU Y it goes to the
39
* C-level do_coprocessor that uses IPI to make CPU X flush T's coprocessors.
40
*/
41
42
#if XTENSA_HAVE_COPROCESSORS
43
44
/*
45
* Macros for lazy context switch.
46
*/
47
48
#define SAVE_CP_REGS(x) \
49
.if XTENSA_HAVE_COPROCESSOR(x); \
50
.align 4; \
51
.Lsave_cp_regs_cp##x: \
52
xchal_cp##x##_store a2 a3 a4 a5 a6; \
53
ret; \
54
.endif
55
56
#define LOAD_CP_REGS(x) \
57
.if XTENSA_HAVE_COPROCESSOR(x); \
58
.align 4; \
59
.Lload_cp_regs_cp##x: \
60
xchal_cp##x##_load a2 a3 a4 a5 a6; \
61
ret; \
62
.endif
63
64
#define CP_REGS_TAB(x) \
65
.if XTENSA_HAVE_COPROCESSOR(x); \
66
.long .Lsave_cp_regs_cp##x; \
67
.long .Lload_cp_regs_cp##x; \
68
.else; \
69
.long 0, 0; \
70
.endif; \
71
.long THREAD_XTREGS_CP##x
72
73
#define CP_REGS_TAB_SAVE 0
74
#define CP_REGS_TAB_LOAD 4
75
#define CP_REGS_TAB_OFFSET 8
76
77
__XTENSA_HANDLER
78
79
SAVE_CP_REGS(0)
80
SAVE_CP_REGS(1)
81
SAVE_CP_REGS(2)
82
SAVE_CP_REGS(3)
83
SAVE_CP_REGS(4)
84
SAVE_CP_REGS(5)
85
SAVE_CP_REGS(6)
86
SAVE_CP_REGS(7)
87
88
LOAD_CP_REGS(0)
89
LOAD_CP_REGS(1)
90
LOAD_CP_REGS(2)
91
LOAD_CP_REGS(3)
92
LOAD_CP_REGS(4)
93
LOAD_CP_REGS(5)
94
LOAD_CP_REGS(6)
95
LOAD_CP_REGS(7)
96
97
.align 4
98
.Lcp_regs_jump_table:
99
CP_REGS_TAB(0)
100
CP_REGS_TAB(1)
101
CP_REGS_TAB(2)
102
CP_REGS_TAB(3)
103
CP_REGS_TAB(4)
104
CP_REGS_TAB(5)
105
CP_REGS_TAB(6)
106
CP_REGS_TAB(7)
107
108
/*
109
* Entry condition:
110
*
111
* a0: trashed, original value saved on stack (PT_AREG0)
112
* a1: a1
113
* a2: new stack pointer, original in DEPC
114
* a3: a3
115
* depc: a2, original value saved on stack (PT_DEPC)
116
* excsave_1: dispatch table
117
*
118
* PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC
119
* < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception
120
*/
121
122
ENTRY(fast_coprocessor)
123
124
s32i a3, a2, PT_AREG3
125
126
#ifdef CONFIG_SMP
127
/*
128
* Check if any coprocessor context is live on another CPU
129
* and if so go through the C-level coprocessor exception handler
130
* to flush it to memory.
131
*/
132
GET_THREAD_INFO (a0, a2)
133
l32i a3, a0, THREAD_CPENABLE
134
beqz a3, .Lload_local
135
136
/*
137
* Pairs with smp_wmb in local_coprocessor_release_all
138
* and with both memws below.
139
*/
140
memw
141
l32i a3, a0, THREAD_CPU
142
l32i a0, a0, THREAD_CP_OWNER_CPU
143
beq a0, a3, .Lload_local
144
145
rsr a0, ps
146
l32i a3, a2, PT_AREG3
147
bbci.l a0, PS_UM_BIT, 1f
148
call0 user_exception
149
1: call0 kernel_exception
150
#endif
151
152
/* Save remaining registers a1-a3 and SAR */
153
154
.Lload_local:
155
rsr a3, sar
156
s32i a1, a2, PT_AREG1
157
s32i a3, a2, PT_SAR
158
mov a1, a2
159
rsr a2, depc
160
s32i a2, a1, PT_AREG2
161
162
/* The hal macros require up to 4 temporary registers. We use a3..a6. */
163
164
s32i a4, a1, PT_AREG4
165
s32i a5, a1, PT_AREG5
166
s32i a6, a1, PT_AREG6
167
s32i a7, a1, PT_AREG7
168
s32i a8, a1, PT_AREG8
169
s32i a9, a1, PT_AREG9
170
s32i a10, a1, PT_AREG10
171
172
/* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
173
174
rsr a3, exccause
175
addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
176
177
/* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/
178
179
ssl a3 # SAR: 32 - coprocessor_number
180
movi a2, 1
181
rsr a0, cpenable
182
sll a2, a2
183
or a0, a0, a2
184
wsr a0, cpenable
185
rsync
186
187
/* Get coprocessor save/load table entry (a7). */
188
189
movi a7, .Lcp_regs_jump_table
190
addx8 a7, a3, a7
191
addx4 a7, a3, a7
192
193
/* Retrieve previous owner (a8). */
194
195
rsr a0, excsave1 # exc_table
196
addx4 a0, a3, a0 # entry for CP
197
l32i a8, a0, EXC_TABLE_COPROCESSOR_OWNER
198
199
/* Set new owner (a9). */
200
201
GET_THREAD_INFO (a9, a1)
202
l32i a4, a9, THREAD_CPU
203
s32i a9, a0, EXC_TABLE_COPROCESSOR_OWNER
204
s32i a4, a9, THREAD_CP_OWNER_CPU
205
206
/*
207
* Enable coprocessor for the new owner. (a2 = 1 << CP number)
208
* This can be done before loading context into the coprocessor.
209
*/
210
l32i a4, a9, THREAD_CPENABLE
211
or a4, a4, a2
212
213
/*
214
* Make sure THREAD_CP_OWNER_CPU is in memory before updating
215
* THREAD_CPENABLE
216
*/
217
memw # (2)
218
s32i a4, a9, THREAD_CPENABLE
219
220
beqz a8, 1f # skip 'save' if no previous owner
221
222
/* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
223
224
l32i a10, a8, THREAD_CPENABLE
225
xor a10, a10, a2
226
227
/* Get context save area and call save routine. */
228
229
l32i a2, a7, CP_REGS_TAB_OFFSET
230
l32i a3, a7, CP_REGS_TAB_SAVE
231
add a2, a2, a8
232
callx0 a3
233
234
/*
235
* Make sure coprocessor context and THREAD_CP_OWNER_CPU are in memory
236
* before updating THREAD_CPENABLE
237
*/
238
memw # (3)
239
s32i a10, a8, THREAD_CPENABLE
240
1:
241
/* Get context save area and call load routine. */
242
243
l32i a2, a7, CP_REGS_TAB_OFFSET
244
l32i a3, a7, CP_REGS_TAB_LOAD
245
add a2, a2, a9
246
callx0 a3
247
248
/* Restore all registers and return from exception handler. */
249
250
l32i a10, a1, PT_AREG10
251
l32i a9, a1, PT_AREG9
252
l32i a8, a1, PT_AREG8
253
l32i a7, a1, PT_AREG7
254
l32i a6, a1, PT_AREG6
255
l32i a5, a1, PT_AREG5
256
l32i a4, a1, PT_AREG4
257
258
l32i a0, a1, PT_SAR
259
l32i a3, a1, PT_AREG3
260
l32i a2, a1, PT_AREG2
261
wsr a0, sar
262
l32i a0, a1, PT_AREG0
263
l32i a1, a1, PT_AREG1
264
265
rfe
266
267
ENDPROC(fast_coprocessor)
268
269
.text
270
271
/*
272
* coprocessor_flush(struct thread_info*, index)
273
* a2 a3
274
*
275
* Save coprocessor registers for coprocessor 'index'.
276
* The register values are saved to or loaded from the coprocessor area
277
* inside the task_info structure.
278
*
279
* Note that this function doesn't update the coprocessor_owner information!
280
*
281
*/
282
283
ENTRY(coprocessor_flush)
284
285
abi_entry_default
286
287
movi a4, .Lcp_regs_jump_table
288
addx8 a4, a3, a4
289
addx4 a3, a3, a4
290
l32i a4, a3, CP_REGS_TAB_SAVE
291
beqz a4, 1f
292
l32i a3, a3, CP_REGS_TAB_OFFSET
293
add a2, a2, a3
294
mov a7, a0
295
callx0 a4
296
mov a0, a7
297
1:
298
abi_ret_default
299
300
ENDPROC(coprocessor_flush)
301
302
#endif /* XTENSA_HAVE_COPROCESSORS */
303
304