Setting up repository
[linux-libre-firmware.git] / ath9k_htc / sboot / magpie_1_1 / sboot / athos / src / xtos / int-highpri-dispatcher.S
1 // High-Priority Interrupt Dispatcher Template
2 // $Id: //depot/rel/Cottonwood/Xtensa/OS/xtos/int-highpri-dispatcher.S#4 $
3
4 // Copyright (c) 2004-2010 Tensilica Inc.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included
15 // in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 //
26 // This file allows writing high-priority interrupt handlers in C,
27 // providing convenience at a significant cost in performance.
28 //
29 // By default, this file is included by inth-template.S .
30 // The default Makefile defines _INTERRUPT_LEVEL when assembling
31 // inth-template.S for each medium and high priority interrupt level.
32 //
33 // To use this template file, define a macro called _INTERRUPT_LEVEL
34 // to be the interrupt priority level of the vector, then include this file.
35
36
37 #include <xtensa/coreasm.h>
38 #include "xtos-internal.h"
39
40
41 #if XCHAL_HAVE_INTERRUPTS
42
43 #define INTERRUPT_MASK          XCHAL_INTLEVEL_MASK(_INTERRUPT_LEVEL)
44 #define SINGLE_INTERRUPT        ((INTERRUPT_MASK & (INTERRUPT_MASK - 1)) == 0)
45 #define SINGLE_INT_NUM          XCHAL_INTLEVEL_NUM(_INTERRUPT_LEVEL)
46
47
48 #define INTLEVEL_N_MASK         INTERRUPT_MASK  // mask of interrupts at this priority
49 #define INTLEVEL_N_NUM          SINGLE_INT_NUM  // interrupt number if there is only one
50 #define INTLEVEL_N_BELOW_MASK   XCHAL_INTLEVEL_ANDBELOW_MASK(_INTERRUPT_LEVEL)
51
52 /*  Indicates whether there are multiple interrupts at this interrupt
53  *  priority, ie. mapped to this interrupt vector.
54  *  If there is only one, its number is INTLEVEL_N_NUM
55  */
56 #define MULTIPLE_INTERRUPTS     (!SINGLE_INTERRUPT)
57
58 /*
59  *  High priority interrupt stack frame:
60  */
61 STRUCT_BEGIN
62 STRUCT_FIELD (long,4,HESF_,SAR)
63 STRUCT_FIELD (long,4,HESF_,WINDOWSTART)
64 STRUCT_FIELD (long,4,HESF_,WINDOWBASE)
65 STRUCT_FIELD (long,4,HESF_,EPC1)
66 STRUCT_FIELD (long,4,HESF_,EXCCAUSE)
67 STRUCT_FIELD (long,4,HESF_,EXCVADDR)
68 STRUCT_FIELD (long,4,HESF_,EXCSAVE1)
69 STRUCT_FIELD (long,4,HESF_,VPRI)        /* (XEA1 only) */
70 #if XCHAL_HAVE_MAC16
71 STRUCT_FIELD (long,4,HESF_,ACCLO)
72 STRUCT_FIELD (long,4,HESF_,ACCHI)
73 /*STRUCT_AFIELD(long,4,HESF_,MR, 4)*/
74 #endif
75 #if XCHAL_HAVE_LOOPS
76 STRUCT_FIELD (long,4,HESF_,LCOUNT)
77 STRUCT_FIELD (long,4,HESF_,LBEG)
78 STRUCT_FIELD (long,4,HESF_,LEND)
79 #endif
80 STRUCT_AFIELD(long,4,HESF_,AREG, 64)    /* address registers ar0..ar63 */
81 #define HESF_AR(n)      HESF_AREG+((n)*4)
82 STRUCT_END(HighPriFrame)
83 #define HESF_TOTALSIZE  HighPriFrameSize+32     /* 32 bytes for interrupted code's save areas under SP */
84
85
86 #if XCHAL_HAVE_XEA1 && HAVE_XSR         /* could be made true for T1040 and T1050 */
87 # error "high-priority interrupt stack frame needs adjustment if HAVE_XSR is allowed with XEA1"
88 #endif
89
90
91 #define PRI_N_STACK_SIZE        1024    /* default to 1 kB stack for each level-N handling */
92
93
94         //  Allocate save area and stack:
95         //  (must use .bss, not .comm, because the subsequent .set does not work otherwise)
96         .section .bss, "aw"
97         .align  16
98 LABEL(_Pri_,_Stack):    .space  PRI_N_STACK_SIZE + HESF_TOTALSIZE
99
100 #if HAVE_XSR
101         .data
102         .global LABEL(_Pri_,_HandlerAddress)
103 LABEL(_Pri_,_HandlerAddress):   .space 4
104 #endif
105
106
107         .text
108         .align  4
109         .global LABEL(_Level,FromVector)
110 LABEL(_Level,FromVector):
111         movi    a2, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE      // get ptr to save area
112         // interlock
113
114         //  Save a few registers so we can do some work:
115         s32i    a0,  a2, HESF_AR(0)
116 #if HAVE_XSR
117         //movi  a0, LABEL(_Level,FromVector)            // this dispatcher's address
118         movi    a0, LABEL(_Pri_,_HandlerAddress)        // dispatcher address var.
119         s32i    a1,  a2, HESF_AR(1)
120         l32i    a0, a0, 0                               // get dispatcher address
121         s32i    a3,  a2, HESF_AR(3)
122         xsr     a0, EXCSAVE_LEVEL       // get saved a2, restore dispatcher address
123 #else
124         rsr     a0, EXCSAVE_LEVEL       // get saved a2
125         s32i    a1,  a2, HESF_AR(1)
126         s32i    a3,  a2, HESF_AR(3)
127 #endif
128         s32i    a4,  a2, HESF_AR(4)
129         s32i    a0,  a2, HESF_AR(2)
130
131         //  Save/restore all exception state
132         //  (IMPORTANT:  this code assumes no general exceptions occur
133         //   during the execution of this dispatcher until this state
134         //   is completely saved and from the point it is restored.)
135         //
136         //  Exceptions that may normally occur within the C handler
137         //  include window exceptions (affecting EPC1), alloca exceptions
138         //  (affecting EPC1/EXCCAUSE and its handling uses EXCSAVE1),
139         //  and possibly others depending on the particular C handler
140         //  (possibly needing save/restore of EXCVADDR; and EXCVADDR
141         //   is also possibly corrupted by any access thru an auto-refill
142         //   way on a processor with a full MMU).
143         //
144         rsr     a3, EPC1
145         rsr     a4, EXCCAUSE
146         s32i    a3, a2, HESF_EPC1
147         s32i    a4, a2, HESF_EXCCAUSE
148 #if !XCHAL_HAVE_XEA1
149         rsr     a3, EXCVADDR
150         s32i    a3, a2, HESF_EXCVADDR
151 #endif
152         rsr     a4, EXCSAVE1
153         s32i    a4, a2, HESF_EXCSAVE1
154
155 #ifdef __XTENSA_WINDOWED_ABI__
156         //  Save remainder of entire address register file (!):
157         movi    a0, XCHAL_NUM_AREGS - 8         // how many saved so far
158 #endif
159
160         s32i    a5,  a2, HESF_AR(5)
161         s32i    a6,  a2, HESF_AR(6)
162         s32i    a7,  a2, HESF_AR(7)
163
164 1:      s32i    a8,  a2, HESF_AR(8)
165         s32i    a9,  a2, HESF_AR(9)
166         s32i    a10, a2, HESF_AR(10)
167         s32i    a11, a2, HESF_AR(11)
168         s32i    a12, a2, HESF_AR(12)
169         s32i    a13, a2, HESF_AR(13)
170         s32i    a14, a2, HESF_AR(14)
171         s32i    a15, a2, HESF_AR(15)
172
173 #ifdef __XTENSA_WINDOWED_ABI__
174         addi    a8, a0, -8
175         addi    a10, a2, 8*4
176         rotw    2
177         bnez    a0, 1b                  // loop until done
178
179         rotw    2
180         // back to original a2 ...
181
182         //  Save a few other registers required for C:
183         rsr     a3, WINDOWSTART
184         rsr     a4, WINDOWBASE
185         s32i    a3, a2, HESF_WINDOWSTART
186         s32i    a4, a2, HESF_WINDOWBASE
187
188         //  Setup window registers for first caller:
189         movi    a3, 1
190         movi    a4, 0
191         wsr     a3, WINDOWSTART
192         wsr     a4, WINDOWBASE
193         rsync
194
195         //  Note:  register window has rotated, ie. a0..a15 clobbered.
196
197 #endif /* __XTENSA_WINDOWED_ABI__ */
198
199         movi    a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE      // get ptr to save area (is also initial stack ptr)
200         movi    a0, 0           // mark start of call frames in stack
201
202         //  Critical state saved, a bit more to do to allow window exceptions...
203
204         //  We now have a C-coherent stack and window state.
205         //  Still have to fix PS while making sure interrupts stay disabled
206         //  at the appropriate level (ie. level 2 and below are disabled in this case).
207
208 #if XCHAL_HAVE_XEA1
209         movi    a7, _xtos_intstruct             // address of interrupt management globals
210         rsilft  a3, _INTERRUPT_LEVEL, XTOS_LOCKLEVEL    // lockout
211         movi    a4, ~INTLEVEL_N_BELOW_MASK      // mask out all interrupts at this level or lower
212         l32i    a3, a7, XTOS_VPRI_ENABLED_OFS   // read previous _xtos_vpri_enabled
213         l32i    a5, a7, XTOS_ENABLED_OFS        // read _xtos_enabled
214         s32i    a4, a7, XTOS_VPRI_ENABLED_OFS   // set new _xtos_vpri_enabled (mask interrupts as if at _INTERRUPT_LEVEL)
215         s32i    a3, a1, HESF_VPRI               // save previous vpri
216         movi    a2, 0x50020                     // WOE=1, UM=1, INTLEVEL=0
217         and     a3, a5, a4                      // mask out selected interrupts
218         wsr     a3, INTENABLE                   // disable all low-priority interrupts
219 #else
220         //  Load PS for C code, clear EXCM (NOTE: this step is different for XEA1):
221 # ifdef __XTENSA_CALL0_ABI__
222         movi    a2, 0x00020 + _INTERRUPT_LEVEL  // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=0, RING=0
223 # else
224         movi    a2, 0x50020 + _INTERRUPT_LEVEL  // WOE=1, CALLINC=1, UM=1, INTLEVEL=N, EXCM=0, RING=0
225 # endif
226
227 #endif
228         wsr     a2, PS                          // update PS to enable window exceptions, etc as per above
229         rsync
230
231         //  Okay, window exceptions can now happen (although we have to call
232         //  deep before any will happen because we've reset WINDOWSTART).
233
234         //  Save other state that might get clobbered by C code:
235
236 //////////////////  COMMON DISPATCH CODE BEGIN
237
238         rsr     a14, SAR
239         s32i    a14, a1, HESF_SAR
240 #if XCHAL_HAVE_LOOPS
241         rsr     a14, LCOUNT
242         s32i    a14, a1, HESF_LCOUNT
243         rsr     a14, LBEG
244         s32i    a14, a1, HESF_LBEG
245         rsr     a14, LEND
246         s32i    a14, a1, HESF_LEND
247 #endif
248 #if XCHAL_HAVE_MAC16
249         rsr     a14, ACCLO
250         s32i    a14, a1, HESF_ACCLO
251         rsr     a14, ACCHI
252         s32i    a14, a1, HESF_ACCHI
253 #endif
254
255 #if MULTIPLE_INTERRUPTS         /* > 1 interrupts at this priority */   // _split_ multi_setup
256 #define TABLE_OFS       0
257
258         rsr     a15, INTERRUPT          // mask of pending interrupts
259 # if XCHAL_HAVE_XEA1
260         l32i    a12, a7, XTOS_ENABLED_OFS       // mask of enabled interrupts
261 # else
262         rsr     a12, INTENABLE          // mask of enabled interrupts
263 # endif
264         movi    a13, INTLEVEL_N_MASK    // mask of interrupts at this priority level
265         and     a15, a15, a12
266         and     a15, a15, a13           // enabled & pending interrupts at this priority
267         _beqz   a15, LABEL(Pri_,_spurious)      // handle spurious interrupts (eg. level-trig.)
268 LABEL(Pri_,_loop):                              // handle all enabled & pending interrupts
269         neg     a14, a15
270         and     a14, a14, a15           // single-out least-significant bit set in mask
271         wsr     a14, INTCLEAR           // clear if edge-trig. or s/w or wr/err (else no effect)
272
273         //  Compute pointer to interrupt table entry, given mask a14 with single bit set:
274
275 # if XCHAL_HAVE_NSA
276         movi    a12, _xtos_interrupt_table - (32-XCHAL_NUM_INTERRUPTS)*8
277         nsau    a14, a14                // get index of bit in a14, numbered from msbit
278         addx8   a12, a14, a12
279 # else /* XCHAL_HAVE_NSA */
280         movi    a12, _xtos_interrupt_table      // pointer to interrupt table
281         bltui   a14, 0x10000, 1f        // in 16 lsbits? (if so, check them)
282         addi    a12, a12, 16*8          // no, index is at least 16 entries further
283         // (the above ADDI expands to an ADDI+ADDMI sequence, +128 is outside its range)
284         extui   a14, a14, 16,16         // shift right upper 16 bits
285 1:      bltui   a14, 0x100, 1f          // in 8 lsbits? (if so, check them)
286         addi    a12, a12, 8*8           // no, index is at least 8 entries further
287         srli    a14, a14, 8             // shift right upper 8 bits
288 1:      bltui   a14, 0x10, 1f           // in 4 lsbits? (if so, check them)
289         addi    a12, a12, 4*8           // no, index is at least 4 entries further
290         srli    a14, a14, 4             // shift right 4 bits
291 1:      bltui   a14, 0x4, 1f            // in 2 lsbits? (if so, check them)
292         addi    a12, a12, 2*8           // no, index is at least 2 entries further
293         srli    a14, a14, 2             // shift right 2 bits
294 1:      bltui   a14, 0x2, 1f            // is it the lsbit?
295         addi    a12, a12, 1*8           // no, index is one entry further
296 1:                                      // done! a12 points to interrupt's table entry
297 # endif /* XCHAL_HAVE_NSA */
298
299 #else /* !MULTIPLE_INTERRUPTS */
300
301 # if XCHAL_HAVE_NSA
302 #  define TABLE_OFS     8 * (XCHAL_NUM_INTERRUPTS - 1 - INTLEVEL_N_NUM)
303 # else
304 #  define TABLE_OFS     8 * INTLEVEL_N_NUM
305 # endif
306
307         movi    a13, INTLEVEL_N_MASK    // (if interrupt is s/w or edge-triggered or write/err only)
308         movi    a12, _xtos_interrupt_table      // get pointer to its interrupt table entry
309         wsr     a13, INTCLEAR           // clear the interrupt (if s/w or edge or wr/err only)
310
311 #endif /* ifdef MULTIPLE_INTERRUPTS */
312
313         l32i    a13, a12, TABLE_OFS + 0 // get pointer to handler from table entry
314 #ifdef __XTENSA_CALL0_ABI__
315         l32i    a2, a12, TABLE_OFS + 4  // pass single argument to C handler
316         callx0  a13                     // call interrupt's C handler
317 #else
318         l32i    a6, a12, TABLE_OFS + 4  // pass single argument to C handler
319         callx4  a13                     // call interrupt's C handler
320 #endif
321
322 #if XCHAL_HAVE_XEA1
323         movi    a7, _xtos_intstruct     // address of interrupt management globals
324 #endif
325 #if MULTIPLE_INTERRUPTS         /* > 1 interrupts at this priority */
326         rsr     a15, INTERRUPT          // get pending interrupts
327 # if XCHAL_HAVE_XEA1
328         l32i    a12, a7, XTOS_ENABLED_OFS       // get enabled interrupts
329 # else
330         rsr     a12, INTENABLE          // get enabled interrupts
331 # endif
332         movi    a13, INTLEVEL_N_MASK    // get mask of interrupts at this priority level
333         and     a15, a15, a12
334         and     a15, a15, a13           // pending+enabled interrupts at this priority
335         _bnez   a15, LABEL(Pri_,_loop)  // if any remain, dispatch one
336 LABEL(Pri_,_spurious):
337 #endif /* MULTIPLE_INTERRUPTS */
338
339         //  Restore everything, and return.
340
341         //  Three temp registers are required for this code to be optimal (no interlocks) in
342         //  T2xxx microarchitectures with 7-stage pipe; otherwise only two
343         //  registers would be needed.
344         //
345 #if XCHAL_HAVE_LOOPS
346         l32i    a13, a1, HESF_LCOUNT
347         l32i    a14, a1, HESF_LBEG
348         l32i    a15, a1, HESF_LEND
349         wsr     a13, LCOUNT
350         wsr     a14, LBEG
351         wsr     a15, LEND
352 #endif
353
354 #if XCHAL_HAVE_MAC16
355         l32i    a13, a1, HESF_ACCLO
356         l32i    a14, a1, HESF_ACCHI
357         wsr     a13, ACCLO
358         wsr     a14, ACCHI
359 #endif
360         l32i    a15, a1, HESF_SAR
361         wsr     a15, SAR
362
363 //////////////////  COMMON DISPATCH CODE END
364
365 #if XCHAL_HAVE_XEA1
366         //  Here, a7 = address of interrupt management globals
367         l32i    a4, a1, HESF_VPRI               // restore previous vpri
368         rsil    a3, XTOS_LOCKLEVEL              // lockout
369         l32i    a5, a7, XTOS_ENABLED_OFS        // read _xtos_enabled
370         s32i    a4, a7, XTOS_VPRI_ENABLED_OFS   // set new _xtos_vpri_enabled
371         movi    a2, 0x00020 + _INTERRUPT_LEVEL  // WOE=0, UM=1, INTLEVEL=N
372         and     a3, a5, a4                      // mask out selected interrupts
373         wsr     a3, INTENABLE                   // disable all low-priority interrupts
374 #else
375         //  Load PS for interrupt exit, set EXCM:
376         movi    a2, 0x00030 + _INTERRUPT_LEVEL  // WOE=0, CALLINC=0, UM=1, INTLEVEL=N, EXCM=1, RING=0
377 #endif
378         wsr     a2, PS                          // update PS to disable window exceptions, etc as per above
379         rsync
380
381         //  NOTE:  here for XEA1, restore INTENABLE etc...
382
383 #ifdef __XTENSA_WINDOWED_ABI__
384         //  Restore window registers:
385         l32i    a2, a1, HESF_WINDOWSTART
386         l32i    a3, a1, HESF_WINDOWBASE
387         wsr     a2, WINDOWSTART
388         wsr     a3, WINDOWBASE
389         rsync
390         //  Note:  register window has rotated, ie. a0..a15 clobbered.
391
392         //  Reload initial stack pointer:
393         movi    a1, LABEL(_Pri_,_Stack) + PRI_N_STACK_SIZE      // - 16
394         movi    a6, XCHAL_NUM_AREGS - 8         // how many saved so far
395         addi    a7, a1, -8*4
396
397         //  Restore entire register file (!):
398
399 1:
400         addi    a14, a6, -8
401         addi    a15, a7, 8*4
402         l32i    a4, a15, HESF_AR(4)
403         l32i    a5, a15, HESF_AR(5)
404         l32i    a6, a15, HESF_AR(6)
405         l32i    a7, a15, HESF_AR(7)
406         l32i    a8, a15, HESF_AR(8)
407         l32i    a9, a15, HESF_AR(9)
408         l32i    a10,a15, HESF_AR(10)
409         l32i    a11,a15, HESF_AR(11)
410         rotw    2
411         bnez    a6, 1b                  // loop until done
412
413         l32i    a4, a7, HESF_AR(12)
414         l32i    a5, a7, HESF_AR(13)
415         l32i    a6, a7, HESF_AR(14)
416         l32i    a7, a7, HESF_AR(15)
417         rotw    2
418
419         // back to original a1 ...
420
421 #else  /* Call0 ABI: */
422
423         l32i    a4, a1, HESF_AR(4)      // restore general registers
424         l32i    a5, a1, HESF_AR(5)
425         l32i    a6, a1, HESF_AR(6)
426         l32i    a7, a1, HESF_AR(7)
427         l32i    a8, a1, HESF_AR(8)
428         l32i    a9, a1, HESF_AR(9)
429         l32i    a10, a1, HESF_AR(10)
430         l32i    a11, a1, HESF_AR(11)
431         l32i    a12, a1, HESF_AR(12)
432         l32i    a13, a1, HESF_AR(13)
433         l32i    a14, a1, HESF_AR(14)
434         l32i    a15, a1, HESF_AR(15)
435
436 #endif  /* __XTENSA_WINDOWED_ABI__ */
437
438         //  Restore exception state:
439         l32i    a2, a1, HESF_EPC1
440         l32i    a3, a1, HESF_EXCCAUSE
441         wsr     a2, EPC1
442         wsr     a3, EXCCAUSE
443 #if !XCHAL_HAVE_XEA1
444         l32i    a2, a1, HESF_EXCVADDR
445         wsr     a2, EXCVADDR
446 #endif
447         l32i    a3, a1, HESF_EXCSAVE1
448         wsr     a3, EXCSAVE1
449
450         l32i    a0,  a1, HESF_AR(0)
451         l32i    a2,  a1, HESF_AR(2)
452         l32i    a3,  a1, HESF_AR(3)
453         l32i    a1,  a1, HESF_AR(1)
454         rfi     _INTERRUPT_LEVEL
455
456         .size   LABEL(_Level,FromVector), . - LABEL(_Level,FromVector)
457
458         //  This symbol exists solely for the purpose of being able to pull-in this
459         //  dispatcher using _xtos_dispatch_level<n>() routines with the tiny-rt LSP:
460         .global LABEL(_Level,HandlerLabel)
461         .set LABEL(_Level,HandlerLabel), 0
462
463 #endif /* XCHAL_HAVE_INTERRUPTS */
464