1/*
2 * Copyright (c) 2014 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7/**
8 * @file
9 * @brief Handling of transitions to-and-from fast IRQs (FIRQ)
10 *
11 * This module implements the code for handling entry to and exit from Fast IRQs.
12 *
13 * See isr_wrapper.S for details.
14 */
15
16#include <kernel_structs.h>
17#include <offsets_short.h>
18#include <toolchain.h>
19#include <linker/sections.h>
20#include <arch/cpu.h>
21#include <swap_macros.h>
22
23GTEXT(_firq_enter)
24GTEXT(_firq_exit)
25
26/**
27 *
28 * @brief Work to be done before handing control to a FIRQ ISR
29 *
30 * The processor switches to a second register bank so registers from the
31 * current bank do not have to be preserved yet. The only issue is the LP_START/
32 * LP_COUNT/LP_END registers, which are not banked. These can be saved
33 * in available callee saved registers.
34 *
35 * If all FIRQ ISRs are programmed such that there are no use of the LP
36 * registers (ie. no LPcc instruction), and CONFIG_ARC_STACK_CHECKING is
37 * not set, then the kernel can be configured to not save and restore them.
38 *
39 * When entering a FIRQ, interrupts might as well be locked: the processor is
40 * running at its highest priority, and cannot be interrupted by any other
41 * interrupt. An exception, however, can be taken.
42 *
43 * Assumption by _isr_demux: r3 is untouched by _firq_enter.
44 *
45 * @return N/A
46 */
47
48SECTION_FUNC(TEXT, _firq_enter)
49/*
50 * ATTENTION:
51 * If CONFIG_RGF_NUM_BANKS>1, firq uses a 2nd register bank so GPRs do
52 * not need to be saved.
53 * If CONFIG_RGF_NUM_BANKS==1, firq must use the stack to save registers.
54 * This has already been done by _isr_wrapper.
55 */
56#ifdef CONFIG_ARC_STACK_CHECKING
57#ifdef CONFIG_ARC_SECURE_FIRMWARE
58	lr r2, [_ARC_V2_SEC_STAT]
59	bclr r2, r2, _ARC_V2_SEC_STAT_SSC_BIT
60	sflag r2
61#else
62	/* disable stack checking */
63	lr r2, [_ARC_V2_STATUS32]
64	bclr r2, r2, _ARC_V2_STATUS32_SC_BIT
65	kflag r2
66#endif
67#endif
68
69#if CONFIG_RGF_NUM_BANKS != 1
70	/*
71	 * Save LP_START/LP_COUNT/LP_END because called handler might use.
72	 * Save these in callee saved registers to avoid using memory.
73	 * These will be saved by the compiler if it needs to spill them.
74	 */
75	mov r23,lp_count
76	lr r24, [_ARC_V2_LP_START]
77	lr r25, [_ARC_V2_LP_END]
78#endif
79
80	/* check whether irq stack is used */
81	_check_and_inc_int_nest_counter r0, r1
82
83	bne.d firq_nest
84	mov_s r0, sp
85
86	_get_curr_cpu_irq_stack sp
87#if CONFIG_RGF_NUM_BANKS != 1
88	b firq_nest_1
89firq_nest:
90	/*
91	 * because firq and rirq share the same interrupt stack,
92	 * switch back to original register bank to get correct sp.
93	 * to get better firq latency, an approach is to prepare
94	 * separate interrupt stack for firq and do not do thread
95	 * switch in firq.
96	 */
97	lr r1, [_ARC_V2_STATUS32]
98	and r1, r1, ~_ARC_V2_STATUS32_RB(7)
99	kflag r1
100
101	/* here use _ARC_V2_USER_SP and ilink to exchange sp
102	 * save original value of _ARC_V2_USER_SP and ilink into
103	 * the stack of interrupted context first, then restore them later
104	 */
105	push ilink
106	PUSHAX ilink, _ARC_V2_USER_SP
107
108	/* sp here is the sp of interrupted context */
109	sr sp, [_ARC_V2_USER_SP]
110	/* here, bank 0 sp must go back to the value before push and
111	 * PUSHAX as we will switch to bank1, the pop and POPAX later will
112	 * change bank1's sp, not bank0's sp
113	 */
114	add sp, sp, 8
115
116	/* switch back to banked reg, only ilink can be used */
117	lr ilink, [_ARC_V2_STATUS32]
118	or ilink, ilink, _ARC_V2_STATUS32_RB(1)
119	kflag ilink
120	lr sp, [_ARC_V2_USER_SP]
121
122	POPAX ilink, _ARC_V2_USER_SP
123	pop ilink
124firq_nest_1:
125#else
126firq_nest:
127#endif
128	push_s r0
129	j _isr_demux
130
131
132
133/**
134 *
135 * @brief Work to be done exiting a FIRQ
136 *
137 * @return N/A
138 */
139
140SECTION_FUNC(TEXT, _firq_exit)
141
142#if CONFIG_RGF_NUM_BANKS != 1
143	/* restore lp_count, lp_start, lp_end from r23-r25 */
144	mov lp_count,r23
145	sr r24, [_ARC_V2_LP_START]
146	sr r25, [_ARC_V2_LP_END]
147#endif
148	_dec_int_nest_counter r0, r1
149
150	_check_nest_int_by_irq_act r0, r1
151
152	jne _firq_no_switch
153
154	/* sp is struct k_thread **old of z_arc_switch_in_isr
155	 * which is a wrapper of z_get_next_switch_handle.
156	 * r0 contains the 1st thread in ready queue. if
157	 * it equals _current(r2) ,then do swap, or no swap.
158	 */
159	_get_next_switch_handle
160
161	cmp r0, r2
162	bne _firq_switch
163
164	/* fall to no switch */
165
166.align 4
167_firq_no_switch:
168	/* restore interrupted context' sp */
169	pop sp
170	/*
171	 * Keeping this code block close to those that use it allows using brxx
172	 * instruction instead of a pair of cmp and bxx
173	 */
174#if CONFIG_RGF_NUM_BANKS == 1
175	_pop_irq_stack_frame
176#endif
177	rtie
178
179.align 4
180_firq_switch:
181	/* restore interrupted context' sp */
182	pop sp
183
184#if CONFIG_RGF_NUM_BANKS != 1
185/*
186 * save r0, r2 in irq stack for a while, as they will be changed by register
187 * bank switch
188 */
189	_get_curr_cpu_irq_stack r1
190	st r0, [r1, -4]
191	st r2, [r1, -8]
192
193	/*
194	 * We know there is no interrupted interrupt of lower priority at this
195	 * point, so when switching back to register bank 0, it will contain the
196	 * registers from the interrupted thread.
197	 */
198#if defined(CONFIG_USERSPACE)
199/* when USERSPACE is configured, here need to consider the case where firq comes
200 * out in user mode, according to ARCv2 ISA and nsim, the following micro ops
201 * will be executed:
202 *	sp<-reg bank1'sp
203 *      switch between sp and _ARC_V2_USER_SP
204 * then:
205 *      sp is the sp of kernel stack of interrupted thread
206 *      _ARC_V2_USER_SP is reg bank1'sp
207 *      the sp of user stack of interrupted thread is reg bank0'sp
208 * if firq comes out in kernel mode, the following micro ops will be executed:
209 *      sp<-reg bank'sp
210 * so, sw needs to do necessary handling to set up the correct sp
211 */
212	lr r0, [_ARC_V2_AUX_IRQ_ACT]
213	bbit0 r0, 31, _firq_from_kernel
214	aex sp, [_ARC_V2_USER_SP]
215	lr r0, [_ARC_V2_STATUS32]
216	and r0, r0, ~_ARC_V2_STATUS32_RB(7)
217	kflag r0
218	aex sp, [_ARC_V2_USER_SP]
219	b _firq_create_irq_stack_frame
220_firq_from_kernel:
221#endif
222	/* chose register bank #0 */
223	lr r0, [_ARC_V2_STATUS32]
224	and r0, r0, ~_ARC_V2_STATUS32_RB(7)
225	kflag r0
226
227_firq_create_irq_stack_frame:
228	/* we're back on the outgoing thread's stack */
229	_create_irq_stack_frame
230
231	/*
232	 * In a FIRQ, STATUS32 of the outgoing thread is in STATUS32_P0 and the
233	 * PC in ILINK: save them in status32/pc respectively.
234	 */
235
236	lr r0, [_ARC_V2_STATUS32_P0]
237	st_s r0, [sp, ___isf_t_status32_OFFSET]
238
239	st ilink, [sp, ___isf_t_pc_OFFSET] /* ilink into pc */
240/*
241 * load r0, r2 from irq stack
242 */
243	_get_curr_cpu_irq_stack r1
244	ld r0, [r1, -4]
245	ld r2, [r1, -8]
246#endif
247	/* r2 is old thread */
248	_irq_store_old_thread_callee_regs
249
250	st _CAUSE_FIRQ, [r2, _thread_offset_to_relinquish_cause]
251
252	/* mov new thread (r0) to r2 */
253
254	mov r2, r0
255	_load_new_thread_callee_regs
256
257	breq r3, _CAUSE_RIRQ, _firq_switch_from_rirq
258	nop_s
259	breq r3, _CAUSE_FIRQ, _firq_switch_from_firq
260	nop_s
261
262	/* fall through */
263
264.align 4
265_firq_switch_from_coop:
266
267	_set_misc_regs_irq_switch_from_coop
268
269	/* pc into ilink */
270	pop_s r0
271	mov ilink, r0
272
273	pop_s r0 /* status32 into r0 */
274	sr r0, [_ARC_V2_STATUS32_P0]
275
276#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING
277	push_s blink
278
279	bl z_thread_mark_switched_in
280
281	pop_s blink
282#endif
283	rtie
284
285.align 4
286_firq_switch_from_rirq:
287_firq_switch_from_firq:
288
289	_set_misc_regs_irq_switch_from_irq
290
291	_pop_irq_stack_frame
292
293	ld ilink, [sp, -4] /* status32 into ilink */
294	sr ilink, [_ARC_V2_STATUS32_P0]
295	ld ilink, [sp, -8] /* pc into ilink */
296
297#ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING
298	push_s blink
299
300	bl z_thread_mark_switched_in
301
302	pop_s blink
303#endif
304	/* LP registers are already restored, just switch back to bank 0 */
305	rtie
306