1// XEA1 - Level-one interrupt dispatcher (user vectored handler)
2
3// Copyright (c) 1999-2016 Tensilica Inc.
4//
5// Permission is hereby granted, free of charge, to any person obtaining
6// a copy of this software and associated documentation files (the
7// "Software"), to deal in the Software without restriction, including
8// without limitation the rights to use, copy, modify, merge, publish,
9// distribute, sublicense, and/or sell copies of the Software, and to
10// permit persons to whom the Software is furnished to do so, subject to
11// the following conditions:
12//
13// The above copyright notice and this permission notice shall be included
14// in all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24#include <sof/lib/memory.h>
25
26#include <xtensa/coreasm.h>
27#include "../xtos-internal.h"
28
29#if XCHAL_HAVE_XEA1 && XCHAL_HAVE_EXCEPTIONS && XCHAL_HAVE_INTERRUPTS
30
31#define _INTERRUPT_LEVEL	1
32
33
34	//  NOTE:  something equivalent to the following vector is executed
35	//  before entering this handler (see user-vector.S).
36//_UserExceptionVector:
37//	addi	a1, a1, -ESF_TOTALSIZE	// allocate exception stack frame, etc.
38//	s32i	a2, a1, UEXC_a2
39//	s32i	a3, a1, UEXC_a3
40//	movi	a3, xtos_exc_handler_table
41//	rsr.exccause	a2
42//	addx4	a2, a2, a3
43//	l32i	a2, a2, 0
44//	s32i	a4, a1, UEXC_a4
45//	jx	a2		// jump to cause-specific handler
46
47	.global	_need_user_vector_	// pull-in real user vector (tiny LSP)
48
49	.text
50	.align	4
51	.global	_xtos_l1int_handler
52_xtos_l1int_handler:
53	//  HERE:  a2, a3, a4 have been saved to exception stack frame allocated with a1 (sp).
54
55	s32i	a5, a1, UEXC_a5		// a5 will get clobbered by ENTRY after pseudo-CALL4
56					//   (a4..a15 spilled as needed; save if modified)
57
58#if HAVE_XSR
59	movi	a2, PS_WOECALL4_ABI|PS_UM|PS_INTLEVEL(XCHAL_EXCM_LEVEL)
60	xsr.ps	a2
61	s32i	a2, a1, UEXC_ps
62#else
63	rsr.ps	a2
64	s32i	a2, a1, UEXC_ps
65	movi	a2, PS_WOECALL4_ABI|PS_UM|PS_INTLEVEL(XCHAL_EXCM_LEVEL)
66	wsr.ps	a2
67#endif
68	rsync
69
70	/* store pc */
71	rsr.epc1	a2
72	s32i	a2, a1, UEXC_pc
73
74	/* store rest of the registers */
75	s32i	a0, a1, UEXC_a0
76	s32i	a6, a1, UEXC_a6
77	s32i	a7, a1, UEXC_a7
78	s32i	a8, a1, UEXC_a8
79	s32i	a9, a1, UEXC_a9
80	s32i	a10, a1, UEXC_a10
81	s32i	a11, a1, UEXC_a11
82	s32i	a12, a1, UEXC_a12
83	s32i	a13, a1, UEXC_a13
84	s32i	a14, a1, UEXC_a14
85	s32i	a15, a1, UEXC_a15
86
87	/* store current sp */
88	xtos_addr_percore	a2, xtos_saved_sp
89	s32i	a1, a2, 0
90
91	/* store current task sp */
92	xtos_task_ctx_percore	a2
93	beqz	a2, no_context
94	s32i	a1, a2, TC_stack_pointer
95
96no_context:
97#  if XTOS_CNEST
98	l32i	a2, a1, ESF_TOTALSIZE-20	// save nested-C-func call-chain ptr
99#  endif
100	addi	a1, a1, ESF_TOTALSIZE
101#  if XTOS_DEBUG_PC
102	rsr.epc1	a4	// [for debug] get return PC
103	movi	a5, 0xC0000000		// [for debug] setup call size...
104	or	a4, a5, a4		// [for debug] set upper two bits of return PC
105	addx2	a4, a5, a4		// [for debug] clear upper bit
106#  else
107	movi	a4, 0			/* terminate stack frames, overflow check */
108#  endif
109	_entry	a1, ESF_TOTALSIZE
110
111/* Reset the interrupt level to xtos locklevel (lvl 6 on most systems) */
112
113	rsil	a15, XTOS_LOCKLEVEL
114
115/* Get bit list of pending interrupts at the current interrupt priority level.
116 * If bit list is empty, interrupt is spurious (can happen if a
117 * genuine interrupt brings control this direction, but the interrupt
118 * goes away before we read the INTERRUPT register).  Also save off
119 * sar, loops, mac16 registers and coprocessors. */
120
121#if __XCC__
122#if (XCHAL_CP_MASK & CP0_MASK)
123	mov	a11, a1
124	addi	a11, a11, UEXC_cp0
125	xchal_cp0_store	a11, a12, a13, a14, a15
126#endif
127#if (XCHAL_CP_MASK & CP1_MASK)
128	mov	a11, a1
129	addi	a11, a11, UEXC_cp1
130	xchal_cp1_store a11, a12, a13, a14, a15
131#endif
132#endif
133	rsr.interrupt	a15
134	rsr.intenable	a12
135	movi	a13, XCHAL_INTLEVEL1_MASK
136	and	a15, a15, a12
137	and	a15, a15, a13
138	rsr.sar	a14
139	s32i	a14, a1, UEXC_sar
140	save_loops_mac16	a1, a13, a14
141
142	/* switch to interrupt stack */
143	xtos_int_stack_addr_percore a13, _INTERRUPT_LEVEL, xtos_stack_for_interrupt
144	s32i	a1, a13, 0
145	addi	a1, a13, SOF_STACK_SIZE
146
147	_beqz	a15, LABEL(spurious,int)
148
149	/* set stack base and size for interrupt context */
150	xtos_addr_percore	a11, xtos_interrupt_ctx
151	s32i	a13, a11, TC_stack_base
152	movi	a13, SOF_STACK_SIZE
153	s32i	a13, a11, TC_stack_size
154
155	/* save task context */
156	xtos_task_ctx_percore	a13
157	xtos_store_percore	a13, a14, xtos_saved_ctx
158
159	/* set interrupt task context */
160	xtos_task_ctx_store_percore	a11, a14
161
162	xtos_on_wakeup
163
164/* Loop to handle all pending interrupts. */
165
166LABEL(.L1,_loop0):
167	neg	a12, a15
168	and	a12, a12, a15
169	wsr.intclear	a12	// clear if edge-trig or s/w or wr/err (else no effect)
170#if CONFIG_MULTICORE
171	xtos_addr_percore	a13, xtos_interrupt_table
172#else
173	movi	a13, xtos_interrupt_table
174#endif
175	find_ms_setbit	a15, a12, a14, 0
176	mapint	a15
177	addx8	a12, a15, a13
178	l32i	a13, a12, XIE_HANDLER
179	l32i	a14, a12, XIE_ARG
180	mov	a15, a1
181	callx12	a13
182
183	rsr.interrupt	a15
184	rsr.intenable	a12
185	movi	a13, XCHAL_INTLEVEL1_MASK
186	and	a15, a15, a12
187	and	a15, a15, a13
188	_bnez	a15, LABEL(.L1,_loop0)
189
190/* Restore everything, and return. */
191
192	/* restore task context if needed */
193	xtos_task_ctx_percore	a11
194	xtos_addr_percore	a12, xtos_interrupt_ctx
195	bne	a11, a12, restore_cp
196	xtos_addr_percore	a12, xtos_saved_ctx
197	xtos_task_ctx_store_percore	a12, a11
198
199restore_cp:
200#if __XCC__
201#if (XCHAL_CP_MASK & CP0_MASK)
202	xtos_task_ctx_percore	a11
203	beqz	a11, no_context_2
204	l32i	a11, a11, TC_stack_pointer
205	addi	a11, a11, UEXC_cp0
206	xchal_cp0_load	a11, a12, a13, a14, a15
207#endif
208#if (XCHAL_CP_MASK & CP1_MASK)
209	xtos_task_ctx_percore	a11
210	beqz	a11, no_context_2
211	l32i	a11, a11, TC_stack_pointer
212	addi	a11, a11, UEXC_cp1
213	xchal_cp1_load	a11, a12, a13, a14, a15
214#endif
215#endif
216
217no_context_2:
218	restore_loops_mac16	a1, a13, a14, a15
219	l32i	a14, a1, UEXC_sar
220LABEL(spurious,int):
221
222#if XCHAL_HAVE_EXCLUSIVE
223	// Clear exclusive monitors.
224	clrex
225#endif
226
227	movi	a0, LABEL(return,from_exc)
228	movi	a13, 0xC0000000
229	wsr.sar	a14
230	or	a0, a0, a13
231	addx2	a0, a13, a0
232# if _INTERRUPT_LEVEL < XCHAL_EXCM_LEVEL
233/* Raise the interrupt mask before
234 * returning to avoid a race condition where we deallocate the
235 * exception stack frame but still have more register values to
236 * restore from it. */
237	rsil	a14, XCHAL_EXCM_LEVEL
238# endif
239	retw
240LABEL(return,from_exc):
241	/* a5 contains interrupt stack pointer */
242	addi	a5, a5, -SOF_STACK_SIZE
243	l32i	a5, a5, 0
244
245# if XTOS_CNEST
246	s32i	a2, a5, ESF_TOTALSIZE-20	// restore nested-C-func call-chain ptr
247# endif
248
249	/* store sp after returning from handler */
250	s32i	a1, a5, UEXC_a1
251
252restore:
253	/* load registers for window spill */
254	l32i	a4, a5, UEXC_a4
255	l32i	a6, a5, UEXC_a6
256	l32i	a7, a5, UEXC_a7
257	l32i	a8, a5, UEXC_a8
258	l32i	a9, a5, UEXC_a9
259	l32i	a10, a5, UEXC_a10
260	l32i	a11, a5, UEXC_a11
261	l32i	a12, a5, UEXC_a12
262	l32i	a13, a5, UEXC_a13
263	l32i	a14, a5, UEXC_a14
264
265	/* check if switch is needed */
266	xtos_addr_percore	a2, xtos_saved_sp
267	xtos_task_ctx_percore	a1
268	beqz	a1, noSwitch
269	l32i	a1, a1, TC_stack_pointer
270	l32i	a0, a2, 0
271	beq	a0, a1, noSwitch
272
273doSwitch:
274	/* store new task sp */
275	s32i	a1, a2, 0
276
277	/* restore sp of task being preempted */
278	l32i	a1, a5, UEXC_a1
279
280	/* spill register windows to the stack */
281	rsr.ps	a2
282	movi	a3, PS_WOE_MASK
283	xor	a2, a2, a3
284	wsr.ps	a2
285
286	call0	xthal_window_spill_nw
287
288	/* restore previous ps */
289	rsr.ps	a2
290	movi	a3, PS_WOE_MASK
291	or	a2, a2, a3
292	wsr.ps	a2
293
294	/* change stack */
295	xtos_addr_percore	a5, xtos_saved_sp
296	l32i	a5, a5, 0
297	j restore
298
299noSwitch:
300	/* restore ps and pc */
301	l32i	a0, a5, UEXC_ps
302	wsr.ps	a0
303	rsync
304	l32i	a0, a5, UEXC_pc
305	wsr.epc1	a0
306
307	/* restore sar, loops and mac16 registers */
308	l32i	a0, a5, UEXC_sar
309	wsr.sar	a0
310	restore_loops_mac16	a5, a0, a1, a2
311
312	/* restore rest of the registers */
313	l32i	a0, a5, UEXC_a0
314	l32i	a1, a5, UEXC_a1
315	l32i	a2, a5, UEXC_a2
316	l32i	a3, a5, UEXC_a3
317	l32i	a15, a5, UEXC_a15
318	l32i	a5, a5, UEXC_a5
319	rfe
320
321	/* FIXME: what about _LevelOneInterrupt ? */
322	.size	_xtos_l1int_handler, . - _xtos_l1int_handler
323
324#endif /* XCHAL_HAVE_XEA1 && XCHAL_HAVE_EXCEPTIONS && XCHAL_HAVE_INTERRUPTS */
325