summaryrefslogtreecommitdiff
path: root/ports/xtensa/xcc/src/xtensa_coproc_handler.S
blob: 0540d63355135327f27b7ecc5af387bddcc66d53 (plain)
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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/**************************************************************************/
/*   Copyright (c) Cadence Design Systems, Inc.                           */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

/**************************************************************************/
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    Xtensa coprocessor handling routines. This code is only active if   */
/*    one or more coprocessors are present.                               */
/*                                                                        */
/**************************************************************************/


#include <xtensa/config/specreg.h>
#include <xtensa/coreasm.h>

#include "xtensa_context.h"
#include "xtensa_rtos.h"


#if XCHAL_CP_NUM > 0

//-----------------------------------------------------------------------------
//  Coprocessor related state and precomputed values.
//-----------------------------------------------------------------------------

// Table of coprocessor owners, identified by thread's CP save area pointer.
// Zero means coprocessor is not owned.

        .data
        .global _xt_coproc_owner_sa
        .align  16,,XCHAL_CP_MAX << 2   //  minimize crossing cache boundaries
_xt_coproc_owner_sa:
        .rept   XCHAL_CP_MAX
        .word   0
        .endr

// Bitmask table for CP n's enable bit, indexed by coprocessor number.

        .section .rodata, "a"
        .global  _xt_coproc_mask
        .align   16,,8                  // try to keep it all in one cache line
        .set     i, 0
_xt_coproc_mask:
        .rept   XCHAL_CP_MAX
        .long   (i<<16) | (1<<i)        // upper 16-bits = i, lower = bitmask
        .set    i, i+1
        .endr

// Offset to CP n save area in thread's CP save area.

        .global _xt_coproc_sa_offset
        .align  16                      // minimize crossing cache boundaries
_xt_coproc_sa_offset:
        .word   XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA
        .word   XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA


//-----------------------------------------------------------------------------
// _xt_coproc_handler
//
// Handles coprocessor exceptions and manages lazy context switching between
// multiple threads sharing the coprocessor(s).
// Register use:
//   a0 - on entry, return address (must have been called via call0).
//   a1 - pointing to valid exception stack frame.
//   a2 - on entry, must hold coprocessor index. On exit, 0 if OK.
//   a3-a15 - may all be used and trashed by this routine.
//-----------------------------------------------------------------------------

        .text
        .align  4
        .global _xt_coproc_handler

_xt_coproc_handler:

        mov     a7,  a0                         // a7 = return address
        mov     a5,  a2                         // a5 = CP index n

        // Get coprocessor state save area of new owner thread
        call0   XT_RTOS_CP_STATE                // a15 = new owner's save area
        beqz    a15, .L_xt_coproc_invalid       // not in a thread (invalid)
        l32i    a4,  a15, XT_CP_ASA             // actual save area address
        beqz    a4,  .L_xt_coproc_invalid       // thread has no save area

        // Enable the co-processor's bit in CPENABLE
        movi    a0,  _xt_coproc_mask
        rsr     a4,  CPENABLE                   // a4 = CPENABLE
        addx4   a0,  a5, a0                     // a0 = &_xt_coproc_mask[n]
        l32i    a0,  a0, 0                      // a0 = (n << 16) | (1 << n)
        movi    a3,  _xt_coproc_owner_sa
        extui   a2,  a0, 0, 16                  // coprocessor bitmask portion
        or      a4,  a4, a2                     // a4 = CPENABLE | (1 << n)
        wsr     a4,  CPENABLE

        // Get old coprocessor owner thread (save area ptr) and assign new one
        addx4   a3,  a5, a3                     // a3 = &_xt_coproc_owner_sa[n]
        l32i    a2,  a3, 0                      // a2 = old owner's save area
        s32i    a15, a3, 0                      // _xt_coproc_owner_sa[n] = new
        rsync                                   // ensure wsr.CPENABLE is complete

        // Do we need to context-switch this coprocessor ?
        beq     a15, a2, .L_xt_coproc_done      // new owner == old, we're done

        // if no old owner then nothing to save
        beqz    a2,  .L_check_new

        // If old owner not actively using CP then nothing to save.
        l16ui   a4,  a2, XT_CPENABLE            // a4 = old owner's CPENABLE
        bnone   a4,  a0, .L_check_new           // old owner not using CP

.L_save_old:
        // We need to save old owner's coprocessor state
        movi    a5,  _xt_coproc_sa_offset

        // Mark old owner state as no longer active (CPENABLE bit n clear)
        xor     a4,  a4, a0                     // clear CP in old owner's CPENABLE
        s16i    a4,  a2, XT_CPENABLE            // update old owner's CPENABLE

        extui   a4,  a0, 16, 5                  // a4 = CP index = n
        addx4   a5,  a4, a5                     // a5 = &_xt_coproc_sa_offset[n]

        // Mark old owner state as saved (CPSTORED bit n set)
        l16ui   a4,  a2, XT_CPSTORED            // a4 = old owner's CPSTORED
        l32i    a5,  a5, 0                      // a5 = XT_CP[n]_SA offset
        or      a4,  a4, a0                     // set CP in old owner's CPSTORED
        s16i    a4,  a2, XT_CPSTORED            // update old owner's CPSTORED
        l32i    a2,  a2, XT_CP_ASA              // ptr to actual (aligned) save area
        extui   a3,  a0, 16, 5                  // a3 = CP index = n
        add     a2,  a2, a5                     // a2 = old owner's area for CP n

        // The config-specific HAL macro invoked below destroys a2-a6.
        // It is theoretically possible for Xtensa processor designers to write TIE
        // that causes more address registers to be affected, but it is generally
        // unlikely. If that ever happens, more registers needs to be saved/restored
        // around this macro invocation, and the value in a15 needs to be recomputed.

        xchal_cpi_store_funcbody

.L_check_new:
        // Check if any state has to be restored for new owner.
        // NOTE: a15 = new owner's save area, cannot be zero when we get here.

        l16ui   a3,  a15, XT_CPSTORED           // a3 = new owner's CPSTORED
        movi    a4,  _xt_coproc_sa_offset
        bnone   a3,  a0,  .L_check_cs           // full CP not saved, check callee-saved
        xor     a3,  a3,  a0                    // CPSTORED bit is set, clear it
        s16i    a3,  a15, XT_CPSTORED           // update new owner's CPSTORED

        // Adjust new owner's save area pointers to area for CP n.
        extui   a3,  a0, 16, 5                  // a3 = CP index = n
        addx4   a4,  a3, a4                     // a4 = &_xt_coproc_sa_offset[n]
        l32i    a4,  a4, 0                      // a4 = XT_CP[n]_SA
        l32i    a5, a15, XT_CP_ASA              // ptr to actual (aligned) save area
        add     a2,  a4, a5                     // a2 = new owner's area for CP

        // The config-specific HAL macro invoked below destroys a2-a6.
        // It is theoretically possible for Xtensa processor designers to write TIE
        // that causes more address registers to be affected, but it is generally
        // unlikely. If that ever happens, more registers needs to be saved/restored
        // around this macro invocation.

        xchal_cpi_load_funcbody

.L_xt_coproc_done:
        movi    a2,  0                          // a2 <- 0 == OK
.L_xt_coproc_err:
        mov     a0,  a7                         // return address
        ret

.L_check_cs:
        // a0 = CP mask in low bits, a15 = new owner's save area.
        l16ui   a2, a15, XT_CP_CS_ST            // a2 = mask of CPs saved
        bnone   a2,  a0, .L_xt_coproc_done      // if no match then done
        and     a2,  a2, a0                     // a2 = which CPs to restore
        extui   a2,  a2, 0, 8                   // extract low 8 bits
        call0   _xt_coproc_restorecs            // restore CP registers
        j       .L_xt_coproc_done

.L_xt_coproc_invalid:
        // Coprocessor exception occurred outside a thread or the thread
        // did not allocate space to save coprocessor state. Return error.
        movi    a2,  1
        j       .L_xt_coproc_err


//-----------------------------------------------------------------------------
// _tx_thread_coproc_state
//
// Helper function to return the save area for the current thread, if any.
// Returns, in a15, the pointer to the save area if any, else zero.
// If in interrupt context, returns zero. Only uses a15.
// Must be called only via call0.
//-----------------------------------------------------------------------------

        .global     _tx_thread_coproc_state
        .type       _tx_thread_coproc_state,@function
        .align      4

_tx_thread_coproc_state:

        // return ( _tx_thread_system_state == 0 && _tx_thread_current_ptr != 0
        //          ? (&_tx_thread_current_ptr->tx_thread_cp_state) : 0 )

        movi    a15, _tx_thread_system_state    // check if interrupt state
        l32i    a15, a15, 0
        bnez    a15, 1f
        movi    a15, _tx_thread_current_ptr     // check if thread running
        l32i    a15, a15, 0
        beqz    a15, 2f

        // Return base address of current thread's co-prcoessor save area.
        addi    a15, a15, tx_thread_cp_state
        ret
1:
        movi    a15, 0                          // return error
2:
        ret


//-----------------------------------------------------------------------------
// _xt_coproc_savecs
//
// If there is a current thread and it has a coprocessor state save area, then
// save all callee-saved state into this area. This function is called from the
// solicited context switch handler. It calls a system-specific function to get
// the coprocessor save area base address.
//
// Entry conditions:
//    - The thread being switched out is still the current thread.
//    - CPENABLE state reflects which coprocessors are active.
//    - Registers have been saved/spilled already.
//
// Exit conditions:
//    - All necessary CP callee-saved state has been saved.
//    - Registers a7-a15 have been trashed.
//
// Must be called from assembly code only, using CALL0.
//-----------------------------------------------------------------------------

        .global     _xt_coproc_savecs
        .type       _xt_coproc_savecs,@function
        .align      4
_xt_coproc_savecs:

        // At entry, CPENABLE should be showing which CPs are enabled.

        rsr     a11, CPENABLE                   // a11 = which CPs are enabled
        beqz    a11, .Ldone                     // quick exit if none
        mov     a14, a0                         // save return address
        call0   XT_RTOS_CP_STATE                // get address of CP save area
        mov     a0, a14                         // restore return address
        beqz    a15, .Ldone                     // if none then nothing to do
        l32i    a14, a15, XT_CP_ASA             // a14 = base of aligned save area
        beqz    a14, .Ldone                     // no save area, nothing to do
        s16i    a11, a15, XT_CP_CS_ST           // save mask of CPs being stored
        movi    a13, _xt_coproc_sa_offset       // array of CP save offsets
        l32i    a15, a15, XT_CP_ASA             // a15 = base of aligned save area

#if XCHAL_CP0_SA_SIZE
        bbci.l  a11, 0, 2f                      // CP 0 not enabled
        l32i    a14, a13, 0                     // a14 = _xt_coproc_sa_offset[0]
        add     a12, a14, a15                   // a12 = save area for CP 0
        xchal_cp0_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP1_SA_SIZE
        bbci.l  a11, 1, 2f                      // CP 1 not enabled
        l32i    a14, a13, 4                     // a14 = _xt_coproc_sa_offset[1]
        add     a12, a14, a15                   // a12 = save area for CP 1
        xchal_cp1_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP2_SA_SIZE
        bbci.l  a11, 2, 2f
        l32i    a14, a13, 8
        add     a12, a14, a15
        xchal_cp2_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP3_SA_SIZE
        bbci.l  a11, 3, 2f
        l32i    a14, a13, 12
        add     a12, a14, a15
        xchal_cp3_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP4_SA_SIZE
        bbci.l  a11, 4, 2f
        l32i    a14, a13, 16
        add     a12, a14, a15
        xchal_cp4_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP5_SA_SIZE
        bbci.l  a11, 5, 2f
        l32i    a14, a13, 20
        add     a12, a14, a15
        xchal_cp5_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP6_SA_SIZE
        bbci.l  a11, 6, 2f
        l32i    a14, a13, 24
        add     a12, a14, a15
        xchal_cp6_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP7_SA_SIZE
        bbci.l  a11, 7, 2f
        l32i    a14, a13, 28
        add     a12, a14, a15
        xchal_cp7_store a12, a7, a8, a9, a10 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

.Ldone:
        ret


//-----------------------------------------------------------------------------
// _xt_coproc_restorecs
//
// Restore any callee-saved coprocessor state for the incoming thread.
// This function is called from coprocessor exception handling, when giving
// ownership to a thread that solicited a context switch earlier. It calls a
// system-specific function to get the coprocessor save area base address.
//
// Entry conditions:
//    - The incoming thread is set as the current thread.
//    - CPENABLE is set up correctly for all required coprocessors.
//    - a2 = mask of coprocessors to be restored.
//
// Exit conditions:
//    - All necessary CP callee-saved state has been restored.
//    - CPENABLE - unchanged.
//    - Registers a2, a8-a15 have been trashed.
//
// Must be called from assembly code only, using CALL0.
//-----------------------------------------------------------------------------

        .global     _xt_coproc_restorecs
        .type       _xt_coproc_restorecs,@function
        .align      4
_xt_coproc_restorecs:

        mov     a14, a0                         // save return address
        call0   XT_RTOS_CP_STATE                // get address of CP save area
        mov     a0, a14                         // restore return address
        beqz    a15, .Ldone2                    // if none then nothing to do
        l32i    a14, a15, XT_CP_ASA             // a14 = base of aligned save area
        beqz    a14, .Ldone2                    // no save area, nothing to do
        l16ui   a13, a15, XT_CP_CS_ST           // a13 = which CPs have been saved
        xor     a13, a13, a2                    // clear the ones being restored
        s16i    a13, a15, XT_CP_CS_ST           // update saved CP mask
        movi    a13, _xt_coproc_sa_offset       // array of CP save offsets
        l32i    a15, a15, XT_CP_ASA             // a15 = base of aligned save area

#if XCHAL_CP0_SA_SIZE
        bbci.l  a2, 0, 2f                       // CP 0 not enabled
        l32i    a14, a13, 0                     // a14 = _xt_coproc_sa_offset[0]
        add     a12, a14, a15                   // a12 = save area for CP 0
        xchal_cp0_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP1_SA_SIZE
        bbci.l  a2, 1, 2f                       // CP 1 not enabled
        l32i    a14, a13, 4                     // a14 = _xt_coproc_sa_offset[1]
        add     a12, a14, a15                   // a12 = save area for CP 1
        xchal_cp1_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP2_SA_SIZE
        bbci.l  a2, 2, 2f
        l32i    a14, a13, 8
        add     a12, a14, a15
        xchal_cp2_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP3_SA_SIZE
        bbci.l  a2, 3, 2f
        l32i    a14, a13, 12
        add     a12, a14, a15
        xchal_cp3_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP4_SA_SIZE
        bbci.l  a2, 4, 2f
        l32i    a14, a13, 16
        add     a12, a14, a15
        xchal_cp4_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP5_SA_SIZE
        bbci.l  a2, 5, 2f
        l32i    a14, a13, 20
        add     a12, a14, a15
        xchal_cp5_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP6_SA_SIZE
        bbci.l  a2, 6, 2f
        l32i    a14, a13, 24
        add     a12, a14, a15
        xchal_cp6_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

#if XCHAL_CP7_SA_SIZE
        bbci.l  a2, 7, 2f
        l32i    a14, a13, 28
        add     a12, a14, a15
        xchal_cp7_load a12, a8, a9, a10, a11 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL
2:
#endif

.Ldone2:
        ret


#if XCHAL_HAVE_XEA3

//-----------------------------------------------------------------------------
//  For XEA3, coprocessor exceptions come here. This is a wrapper function that
//  calls _xt_coproc_handler() to do the actual work. We don't want the handler
//  to be interrupted because that might cause a round-robin switch and leave
//  coprocessor context in a confused state. So interrupts are disabled before
//  calling the handler. They will be re-enabled on return from exception.
//-----------------------------------------------------------------------------

        .text
        .global     _xt_coproc_exc
        .type       _xt_coproc_exc,@function
        .align      4

_xt_coproc_exc:
#ifdef __XTENSA_CALL0_ABI__
    addi    a1,  a1, -16                    // reserve 16 bytes on stack
    s32i    a0,  a1, 0                      // save return address
    s32i   a15,  a1, 8                      // must save a15 (see dispatch)
    movi    a3,  PS_DI_MASK
    xps     a3,  a3                         // Set PS.DI, disable interrupts
    l32i    a3,  a2, XT_STK_EXCCAUSE        // a3 <- exccause
    extui   a2,  a3, 8, 4                   // a2 <- CP index
    call0   _xt_coproc_handler
    l32i    a0,  a1, 0                      // restore return address
    l32i   a15,  a1, 8                      // restore a15
    addi    a1,  a1, 16
    ret
#else
    entry   a1,  48                         // reserve 16 bytes on stack
    s32i    a0,  a1, 0                      // save return address
    movi    a3,  PS_DI_MASK
    xps     a3,  a3                         // Set PS.DI, disable interrupts
    l32i    a3,  a2, XT_STK_EXCCAUSE        // a3 <- exccause
    extui   a2,  a3, 8, 4                   // a2 <- CP index
    call0   _xt_coproc_handler
    l32i    a0,  a1, 0                      // restore return address
    retw
#endif

#endif // XCHAL_HAVE_XEA3


#if XCHAL_HAVE_XEA2

//-----------------------------------------------------------------------------
//  XEA2 coprocessor exception dispatcher. Save enough state to be able to call
//  the coprocessor handler, then restore and return.
//-----------------------------------------------------------------------------

        .text
        .global     _xt_coproc_exc
        .type       _xt_coproc_exc,@function
        .align      4

_xt_coproc_exc:

        mov     a0, sp                          // Allocate stack frame
        addi    sp, sp, -XT_STK_FRMSZ
        s32i    a0, sp, XT_STK_A1               // save SP
#if XCHAL_HAVE_WINDOWED
        s32e    a0, sp, -12                     // for debug backtrace
#endif
        rsr     a0, PS
        s32i    a0, sp, XT_STK_PS               // save PS
        rsr     a0, EPC_1
        s32i    a0, sp, XT_STK_PC               // save PC
        rsr     a0, EXCSAVE_1
        s32i    a0, sp, XT_STK_A0               // retrieve and save a0
#if XCHAL_HAVE_WINDOWED
        s32e    a0, sp, -16                     // for debug backtrace
#endif
        s32i    a2, sp, XT_STK_A2
        s32i    a3, sp, XT_STK_A3
        s32i    a4, sp, XT_STK_A4
        s32i    a5, sp, XT_STK_A5
        s32i    a6, sp, XT_STK_A6
        s32i    a7, sp, XT_STK_A7
        s32i    a8, sp, XT_STK_A8
        s32i    a9, sp, XT_STK_A9
        s32i    a10, sp, XT_STK_A10
        s32i    a11, sp, XT_STK_A11
        s32i    a12, sp, XT_STK_A12
        s32i    a13, sp, XT_STK_A13
        s32i    a14, sp, XT_STK_A14
        s32i    a15, sp, XT_STK_A15

        rsr     a3, EXCCAUSE                    // a3 <- exccause
        addi    a2, a3, -EXCCAUSE_CP0_DISABLED  // a2 <- CP index
        call0   _xt_coproc_handler

        mov     a0, a2                          // save return value
        l32i    a2, sp, XT_STK_A2
        l32i    a3, sp, XT_STK_A3
        l32i    a4, sp, XT_STK_A4
        l32i    a5, sp, XT_STK_A5
        l32i    a6, sp, XT_STK_A6
        l32i    a7, sp, XT_STK_A7
        l32i    a8, sp, XT_STK_A8
        l32i    a9, sp, XT_STK_A9
        l32i    a10, sp, XT_STK_A10
        l32i    a11, sp, XT_STK_A11
        l32i    a12, sp, XT_STK_A12
        l32i    a13, sp, XT_STK_A13
        l32i    a14, sp, XT_STK_A14
        l32i    a15, sp, XT_STK_A15
        bnez    a0, .Lfail                      // abort if failure
        l32i    a0, sp, XT_STK_PC
        wsr     a0, EPC_1                       // restore PC
        l32i    a0, sp, XT_STK_PS
        wsr     a0, PS                          // restore PS
        l32i    a0, sp, XT_STK_A0
        addi    a1, a1, XT_STK_FRMSZ            // deallocate stack frame
        rfe

.Lfail:
        call0   _xt_panic

#endif // XCHAL_HAVE_XEA2

#endif // XCHAL_CP_NUM > 0