hc
2023-12-09 b22da3d8526a935aa31e086e63f60ff3246cb61c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/kernel/iwmmxt.S
 *
 *  XScale iWMMXt (Concan) context switching and handling
 *
 *  Initial code:
 *  Copyright (c) 2003, Intel Corporation
 *
 *  Full lazy switching support, optimizations and more, by Nicolas Pitre
*   Copyright (c) 2003-2004, MontaVista Software, Inc.
 */
 
#include <linux/linkage.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/assembler.h>
#include "iwmmxt.h"
 
#if defined(CONFIG_CPU_PJ4) || defined(CONFIG_CPU_PJ4B)
#define PJ4(code...)        code
#define XSC(code...)
#elif defined(CONFIG_CPU_MOHAWK) || \
   defined(CONFIG_CPU_XSC3) || \
   defined(CONFIG_CPU_XSCALE)
#define PJ4(code...)
#define XSC(code...)        code
#else
#error "Unsupported iWMMXt architecture"
#endif
 
#define MMX_WR0             (0x00)
#define MMX_WR1             (0x08)
#define MMX_WR2             (0x10)
#define MMX_WR3            (0x18)
#define MMX_WR4             (0x20)
#define MMX_WR5             (0x28)
#define MMX_WR6             (0x30)
#define MMX_WR7             (0x38)
#define MMX_WR8             (0x40)
#define MMX_WR9             (0x48)
#define MMX_WR10        (0x50)
#define MMX_WR11        (0x58)
#define MMX_WR12        (0x60)
#define MMX_WR13        (0x68)
#define MMX_WR14        (0x70)
#define MMX_WR15        (0x78)
#define MMX_WCSSF        (0x80)
#define MMX_WCASF        (0x84)
#define MMX_WCGR0        (0x88)
#define MMX_WCGR1        (0x8C)
#define MMX_WCGR2        (0x90)
#define MMX_WCGR3        (0x94)
 
#define MMX_SIZE        (0x98)
 
   .text
   .arm
 
/*
 * Lazy switching of Concan coprocessor context
 *
 * r10 = struct thread_info pointer
 * r9  = ret_from_exception
 * lr  = undefined instr exit
 *
 * called from prefetch exception handler with interrupts enabled
 */
 
ENTRY(iwmmxt_task_enable)
   inc_preempt_count r10, r3
 
   XSC(mrc    p15, 0, r2, c15, c1, 0)
   PJ4(mrc p15, 0, r2, c1, c0, 2)
   @ CP0 and CP1 accessible?
   XSC(tst    r2, #0x3)
   PJ4(tst    r2, #0xf)
   bne    4f                @ if so no business here
   @ enable access to CP0 and CP1
   XSC(orr    r2, r2, #0x3)
   XSC(mcr    p15, 0, r2, c15, c1, 0)
   PJ4(orr    r2, r2, #0xf)
   PJ4(mcr    p15, 0, r2, c1, c0, 2)
 
   ldr    r3, =concan_owner
   add    r0, r10, #TI_IWMMXT_STATE    @ get task Concan save area
   ldr    r2, [sp, #60]            @ current task pc value
   ldr    r1, [r3]            @ get current Concan owner
   str    r0, [r3]            @ this task now owns Concan regs
   sub    r2, r2, #4            @ adjust pc back
   str    r2, [sp, #60]
 
   mrc    p15, 0, r2, c2, c0, 0
   mov    r2, r2                @ cpwait
   bl    concan_save
 
#ifdef CONFIG_PREEMPT_COUNT
   get_thread_info r10
#endif
4:    dec_preempt_count r10, r3
   ret    r9                @ normal exit from exception
 
concan_save:
 
   teq    r1, #0                @ test for last ownership
   beq    concan_load            @ no owner, skip save
 
   tmrc    r2, wCon
 
   @ CUP? wCx
   tst    r2, #0x1
   beq     1f
 
concan_dump:
 
   wstrw    wCSSF, r1, MMX_WCSSF
   wstrw    wCASF, r1, MMX_WCASF
   wstrw    wCGR0, r1, MMX_WCGR0
   wstrw    wCGR1, r1, MMX_WCGR1
   wstrw    wCGR2, r1, MMX_WCGR2
   wstrw    wCGR3, r1, MMX_WCGR3
 
1:    @ MUP? wRn
   tst    r2, #0x2
   beq    2f
 
   wstrd    wR0,  r1, MMX_WR0
   wstrd    wR1,  r1, MMX_WR1
   wstrd    wR2,  r1, MMX_WR2
   wstrd    wR3,  r1, MMX_WR3
   wstrd    wR4,  r1, MMX_WR4
   wstrd    wR5,  r1, MMX_WR5
   wstrd    wR6,  r1, MMX_WR6
   wstrd    wR7,  r1, MMX_WR7
   wstrd    wR8,  r1, MMX_WR8
   wstrd    wR9,  r1, MMX_WR9
   wstrd    wR10, r1, MMX_WR10
   wstrd    wR11, r1, MMX_WR11
   wstrd    wR12, r1, MMX_WR12
   wstrd    wR13, r1, MMX_WR13
   wstrd    wR14, r1, MMX_WR14
   wstrd    wR15, r1, MMX_WR15
 
2:    teq    r0, #0                @ anything to load?
   reteq    lr                @ if not, return
 
concan_load:
 
   @ Load wRn
   wldrd    wR0,  r0, MMX_WR0
   wldrd    wR1,  r0, MMX_WR1
   wldrd    wR2,  r0, MMX_WR2
   wldrd    wR3,  r0, MMX_WR3
   wldrd    wR4,  r0, MMX_WR4
   wldrd    wR5,  r0, MMX_WR5
   wldrd    wR6,  r0, MMX_WR6
   wldrd    wR7,  r0, MMX_WR7
   wldrd    wR8,  r0, MMX_WR8
   wldrd    wR9,  r0, MMX_WR9
   wldrd    wR10, r0, MMX_WR10
   wldrd    wR11, r0, MMX_WR11
   wldrd    wR12, r0, MMX_WR12
   wldrd    wR13, r0, MMX_WR13
   wldrd    wR14, r0, MMX_WR14
   wldrd    wR15, r0, MMX_WR15
 
   @ Load wCx
   wldrw    wCSSF, r0, MMX_WCSSF
   wldrw    wCASF, r0, MMX_WCASF
   wldrw    wCGR0, r0, MMX_WCGR0
   wldrw    wCGR1, r0, MMX_WCGR1
   wldrw    wCGR2, r0, MMX_WCGR2
   wldrw    wCGR3, r0, MMX_WCGR3
 
   @ clear CUP/MUP (only if r1 != 0)
   teq    r1, #0
   mov     r2, #0
   reteq    lr
 
   tmcr    wCon, r2
   ret    lr
 
ENDPROC(iwmmxt_task_enable)
 
/*
 * Back up Concan regs to save area and disable access to them
 * (mainly for gdb or sleep mode usage)
 *
 * r0 = struct thread_info pointer of target task or NULL for any
 */
 
ENTRY(iwmmxt_task_disable)
 
   stmfd    sp!, {r4, lr}
 
   mrs    ip, cpsr
   orr    r2, ip, #PSR_I_BIT        @ disable interrupts
   msr    cpsr_c, r2
 
   ldr    r3, =concan_owner
   add    r2, r0, #TI_IWMMXT_STATE    @ get task Concan save area
   ldr    r1, [r3]            @ get current Concan owner
   teq    r1, #0                @ any current owner?
   beq    1f                @ no: quit
   teq    r0, #0                @ any owner?
   teqne    r1, r2                @ or specified one?
   bne    1f                @ no: quit
 
   @ enable access to CP0 and CP1
   XSC(mrc    p15, 0, r4, c15, c1, 0)
   XSC(orr    r4, r4, #0x3)
   XSC(mcr    p15, 0, r4, c15, c1, 0)
   PJ4(mrc p15, 0, r4, c1, c0, 2)
   PJ4(orr    r4, r4, #0xf)
   PJ4(mcr    p15, 0, r4, c1, c0, 2)
 
   mov    r0, #0                @ nothing to load
   str    r0, [r3]            @ no more current owner
   mrc    p15, 0, r2, c2, c0, 0
   mov    r2, r2                @ cpwait
   bl    concan_save
 
   @ disable access to CP0 and CP1
   XSC(bic    r4, r4, #0x3)
   XSC(mcr    p15, 0, r4, c15, c1, 0)
   PJ4(bic    r4, r4, #0xf)
   PJ4(mcr    p15, 0, r4, c1, c0, 2)
 
   mrc    p15, 0, r2, c2, c0, 0
   mov    r2, r2                @ cpwait
 
1:    msr    cpsr_c, ip            @ restore interrupt mode
   ldmfd    sp!, {r4, pc}
 
ENDPROC(iwmmxt_task_disable)
 
/*
 * Copy Concan state to given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to store Concan state
 *
 * this is called mainly in the creation of signal stack frames
 */
 
ENTRY(iwmmxt_task_copy)
 
   mrs    ip, cpsr
   orr    r2, ip, #PSR_I_BIT        @ disable interrupts
   msr    cpsr_c, r2
 
   ldr    r3, =concan_owner
   add    r2, r0, #TI_IWMMXT_STATE    @ get task Concan save area
   ldr    r3, [r3]            @ get current Concan owner
   teq    r2, r3                @ does this task own it...
   beq    1f
 
   @ current Concan values are in the task save area
   msr    cpsr_c, ip            @ restore interrupt mode
   mov    r0, r1
   mov    r1, r2
   mov    r2, #MMX_SIZE
   b    memcpy
 
1:    @ this task owns Concan regs -- grab a copy from there
   mov    r0, #0                @ nothing to load
   mov    r2, #3                @ save all regs
   mov    r3, lr                @ preserve return address
   bl    concan_dump
   msr    cpsr_c, ip            @ restore interrupt mode
   ret    r3
 
ENDPROC(iwmmxt_task_copy)
 
/*
 * Restore Concan state from given memory address
 *
 * r0 = struct thread_info pointer of target task
 * r1 = memory address where to get Concan state from
 *
 * this is used to restore Concan state when unwinding a signal stack frame
 */
 
ENTRY(iwmmxt_task_restore)
 
   mrs    ip, cpsr
   orr    r2, ip, #PSR_I_BIT        @ disable interrupts
   msr    cpsr_c, r2
 
   ldr    r3, =concan_owner
   add    r2, r0, #TI_IWMMXT_STATE    @ get task Concan save area
   ldr    r3, [r3]            @ get current Concan owner
   bic    r2, r2, #0x7            @ 64-bit alignment
   teq    r2, r3                @ does this task own it...
   beq    1f
 
   @ this task doesn't own Concan regs -- use its save area
   msr    cpsr_c, ip            @ restore interrupt mode
   mov    r0, r2
   mov    r2, #MMX_SIZE
   b    memcpy
 
1:    @ this task owns Concan regs -- load them directly
   mov    r0, r1
   mov    r1, #0                @ don't clear CUP/MUP
   mov    r3, lr                @ preserve return address
   bl    concan_load
   msr    cpsr_c, ip            @ restore interrupt mode
   ret    r3
 
ENDPROC(iwmmxt_task_restore)
 
/*
 * Concan handling on task switch
 *
 * r0 = next thread_info pointer
 *
 * Called only from the iwmmxt notifier with task preemption disabled.
 */
ENTRY(iwmmxt_task_switch)
 
   XSC(mrc    p15, 0, r1, c15, c1, 0)
   PJ4(mrc    p15, 0, r1, c1, c0, 2)
   @ CP0 and CP1 accessible?
   XSC(tst    r1, #0x3)
   PJ4(tst    r1, #0xf)
   bne    1f                @ yes: block them for next task
 
   ldr    r2, =concan_owner
   add    r3, r0, #TI_IWMMXT_STATE    @ get next task Concan save area
   ldr    r2, [r2]            @ get current Concan owner
   teq    r2, r3                @ next task owns it?
   retne    lr                @ no: leave Concan disabled
 
1:    @ flip Concan access
   XSC(eor    r1, r1, #0x3)
   XSC(mcr    p15, 0, r1, c15, c1, 0)
   PJ4(eor r1, r1, #0xf)
   PJ4(mcr    p15, 0, r1, c1, c0, 2)
 
   mrc    p15, 0, r1, c2, c0, 0
   sub    pc, lr, r1, lsr #32        @ cpwait and return
 
ENDPROC(iwmmxt_task_switch)
 
/*
 * Remove Concan ownership of given task
 *
 * r0 = struct thread_info pointer
 */
ENTRY(iwmmxt_task_release)
 
   mrs    r2, cpsr
   orr    ip, r2, #PSR_I_BIT        @ disable interrupts
   msr    cpsr_c, ip
   ldr    r3, =concan_owner
   add    r0, r0, #TI_IWMMXT_STATE    @ get task Concan save area
   ldr    r1, [r3]            @ get current Concan owner
   eors    r0, r0, r1            @ if equal...
   streq    r0, [r3]            @ then clear ownership
   msr    cpsr_c, r2            @ restore interrupts
   ret    lr
 
ENDPROC(iwmmxt_task_release)
 
   .data
   .align    2
concan_owner:
   .word    0