1/*
2 * Copyright (c) 2014-2015 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7/**
8 * @file
9 * @brief Wrapper around ISRs with logic for context switching
10 *
11 *
12 * Wrapper installed in vector table for handling dynamic interrupts that accept
13 * a parameter.
14 */
15
16#include <offsets_short.h>
17#include <zephyr/toolchain.h>
18#include <zephyr/linker/sections.h>
19#include <zephyr/sw_isr_table.h>
20#include <zephyr/kernel_structs.h>
21#include <zephyr/arch/cpu.h>
22#include <swap_macros.h>
23#include <zephyr/arch/arc/asm-compat/assembler.h>
24
25GTEXT(_isr_wrapper)
26GTEXT(_isr_demux)
27
28#if defined(CONFIG_PM)
29GTEXT(z_pm_save_idle_exit)
30#endif
31
32/*
33The symbols in this file are not real functions, and neither are
34_rirq_enter/_firq_enter: they are jump points.
35
36The flow is the following:
37
38ISR -> _isr_wrapper -- + -> _rirq_enter -> _isr_demux -> ISR -> _rirq_exit
39			 |
40			 + -> _firq_enter -> _isr_demux -> ISR -> _firq_exit
41
42Context switch explanation:
43
44The context switch code is spread in these files:
45
46	isr_wrapper.s, switch.s, swap_macros.h, fast_irq.s, regular_irq.s
47
48IRQ stack frame layout:
49
50	high address
51
52	status32
53		pc
54	lp_count
55	lp_start
56	lp_end
57	blink
58	 r13
59	 ...
60	sp ->    r0
61
62	low address
63
64The context switch code adopts this standard so that it is easier to follow:
65
66	- r2 contains _kernel.current ASAP, and the incoming thread when we
67		transition from outgoing thread to incoming thread
68
69Not loading _kernel into r0 allows loading _kernel without stomping on
70the parameter in r0 in arch_switch().
71
72
73ARCv2 processors have two kinds of interrupts: fast (FIRQ) and regular. The
74official documentation calls the regular interrupts 'IRQs', but the internals
75of the kernel call them 'RIRQs' to differentiate from the 'irq' subsystem,
76which is the interrupt API/layer of abstraction.
77
78For FIRQ, there are two cases, depending upon the value of
79CONFIG_RGF_NUM_BANKS.
80
81CONFIG_RGF_NUM_BANKS==1 case:
82Scratch registers are pushed onto the current stack just as they are with
83RIRQ. See the above frame layout. Unlike RIRQ, the status32_p0 and ilink
84registers are where status32 and the program counter are located, so these
85need to be pushed.
86
87CONFIG_RGF_NUM_BANKS!=1 case:
88The FIRQ handler has its own register bank for general purpose registers,
89and thus it doesn't have to save them on a stack. The 'loop' registers
90(lp_count, lp_end, lp_start), however, are not present in the
91second bank. The handler saves these special registers in unused callee saved
92registers (to avoid stack accesses). It is possible to register a FIRQ
93handler that operates outside of the kernel, but care must be taken to only
94use instructions that only use the banked registers.
95
96The kernel is able to handle transitions to and from FIRQ, RIRQ and threads.
97The contexts are saved 'lazily': the minimum amount of work is
98done upfront, and the rest is done when needed:
99
100o RIRQ
101
102	All needed registers to run C code in the ISR are saved automatically
103	on the outgoing thread's stack: loop, status32, pc, and the caller-
104	saved GPRs. That stack frame layout is pre-determined. If returning
105	to a thread, the stack is popped and no registers have to be saved by
106	the kernel. If a context switch is required, the callee-saved GPRs
107	are then saved in the thread's stack.
108
109o FIRQ
110
111	First, a FIRQ can be interrupting a lower-priority RIRQ: if this is
112	the case, the FIRQ does not take a scheduling decision and leaves it
113	the RIRQ to handle. This limits the amount of code that has to run at
114	interrupt-level.
115
116	CONFIG_RGF_NUM_BANKS==1 case:
117	Registers are saved on the stack frame just as they are for RIRQ.
118	Context switch can happen just as it does in the RIRQ case, however,
119	if the FIRQ interrupted a RIRQ, the FIRQ will return from interrupt
120	and let the RIRQ do the context switch. At entry, one register is
121	needed in order to have code to save other registers. r0 is saved
122	first in the stack and restored later
123
124	CONFIG_RGF_NUM_BANKS!=1 case:
125	During early initialization, the sp in the 2nd register bank is made to
126	refer to _firq_stack. This allows for the FIRQ handler to use its own
127	stack. GPRs are banked, loop registers are saved in unused callee saved
128	regs upon interrupt entry. If returning to a thread, loop registers are
129	restored and the CPU switches back to bank 0 for the GPRs. If a context
130	switch is needed, at this point only are all the registers saved.
131	First, a  stack frame with the same layout as the automatic RIRQ one is
132	created and then the callee-saved GPRs are saved in the stack.
133	status32_p0 and ilink are saved in this case, not status32 and pc.
134	To create the stack frame, the FIRQ handling code must first go back to
135	using bank0 of registers, since that is where the registers containing
136	the exiting thread are saved. Care must be taken not to touch any
137	register before saving them: the only one usable at that point is the
138	stack pointer.
139
140o coop
141
142	When a coop context switch is done, the callee-saved registers are
143	saved in the stack. The other GPRs do not need to be saved, since the
144	compiler has already placed them on the stack.
145
146For restoring the contexts, there are six cases. In all cases, the
147callee-saved registers of the incoming thread have to be restored. Then, there
148are specifics for each case:
149
150From coop:
151
152	o to coop
153
154		Do a normal function call return.
155
156	o to any irq
157
158	The incoming interrupted thread has an IRQ stack frame containing the
159	caller-saved registers that has to be popped. status32 has to be
160	restored, then we jump to the interrupted instruction.
161
162From FIRQ:
163
164	When CONFIG_RGF_NUM_BANKS==1, context switch is done as it is for RIRQ.
165	When CONFIG_RGF_NUM_BANKS!=1, the processor is put back to using bank0,
166	not bank1 anymore, because it had to save the outgoing context from
167	bank0, and now has to load the incoming one into bank0.
168
169	o to coop
170
171	The address of the returning instruction from arch_switch() is loaded
172	in ilink and the saved status32 in status32_p0.
173
174	o to any irq
175
176	The IRQ has saved the caller-saved registers in a stack frame, which
177	must be popped, and status32 and pc loaded in status32_p0 and ilink.
178
179From RIRQ:
180
181	o to coop
182
183	The interrupt return mechanism in the processor expects a stack frame,
184	but the outgoing context did not create one. A fake one is created
185	here, with only the relevant values filled in: pc, status32.
186
187	There is a discrepancy between the ABI from the ARCv2 docs,
188	including the way the processor pushes GPRs in pairs in the IRQ stack
189	frame, and the ABI GCC uses. r13 should be a callee-saved register,
190	but GCC treats it as caller-saved. This means that the processor pushes
191	it in the stack frame along with r12, but the compiler does not save it
192	before entering a function. So, it is saved as part of the callee-saved
193	registers, and restored there, but the processor restores it _a second
194	time_ when popping the IRQ stack frame. Thus, the correct value must
195	also be put in the fake stack frame when returning to a thread that
196	context switched out cooperatively.
197
198	o to any irq
199
200	Both types of IRQs already have an IRQ stack frame: simply return from
201	interrupt.
202 */
203
204SECTION_FUNC(TEXT, _isr_wrapper)
205#ifdef CONFIG_ARC_FIRQ
206#if CONFIG_RGF_NUM_BANKS == 1
207/* free r0 here, use r0 to check whether irq is firq.
208 * for rirq,  as sp will not change and r0 already saved, this action
209 * in fact is useless
210 * for firq,  r0 will be restored later
211 */
212	push r0
213#endif
214	lr r0, [_ARC_V2_AUX_IRQ_ACT]
215	ffs r0, r0
216	cmp r0, 0
217#if CONFIG_RGF_NUM_BANKS == 1
218	bnz rirq_path
219	pop r0
220	/* 1-register bank FIRQ handling must save registers on stack */
221	_create_irq_stack_frame
222	lr r0, [_ARC_V2_STATUS32_P0]
223	st_s r0, [sp, ___isf_t_status32_OFFSET]
224	st ilink, [sp, ___isf_t_pc_OFFSET]
225
226	mov_s r3, _firq_exit
227	mov_s r2, _firq_enter
228	j_s [r2]
229rirq_path:
230	add sp, sp, 4
231	mov_s r3, _rirq_exit
232	mov_s r2, _rirq_enter
233	j_s [r2]
234#else
235	mov.z r3, _firq_exit
236	mov.z r2, _firq_enter
237	mov.nz r3, _rirq_exit
238	mov.nz r2, _rirq_enter
239	j_s [r2]
240#endif
241#else
242	MOVR r3, _rirq_exit
243	MOVR r2, _rirq_enter
244	j_s [r2]
245#endif
246
247/* r0, r1, and r3 will be used in exit_tickless_idle macro */
248.macro exit_tickless_idle
249#if defined(CONFIG_PM)
250	clri r0 /* do not interrupt exiting tickless idle operations */
251	MOVR r1, _kernel
252	breq r3, 0, _skip_pm_save_idle_exit
253
254	st 0, [r1, _kernel_offset_to_idle] /* zero idle duration */
255	PUSHR blink
256	jl z_pm_save_idle_exit
257	POPR blink
258
259_skip_pm_save_idle_exit:
260	seti r0
261#endif
262.endm
263
264/* when getting here, r3 contains the interrupt exit stub to call */
265SECTION_FUNC(TEXT, _isr_demux)
266	PUSHR r3
267
268/* according to ARCv2 ISA, r25, r30, r58, r59 are caller-saved
269 * scratch registers, possibly used by interrupt handlers
270 */
271	PUSHR r25
272	PUSHR r30
273#ifdef CONFIG_ARC_HAS_ACCL_REGS
274	PUSHR r58
275#ifndef CONFIG_64BIT
276	PUSHR r59
277#endif /* !CONFIG_64BIT */
278#endif
279
280#ifdef CONFIG_SCHED_THREAD_USAGE
281	bl z_sched_usage_stop
282#endif
283
284#ifdef CONFIG_TRACING_ISR
285	bl sys_trace_isr_enter
286#endif
287	/* cannot be done before this point because we must be able to run C */
288	exit_tickless_idle
289
290	lr r0, [_ARC_V2_ICAUSE]
291	/* handle software triggered interrupt */
292	lr r3, [_ARC_V2_AUX_IRQ_HINT]
293	brne r3, r0, irq_hint_handled
294	sr 0, [_ARC_V2_AUX_IRQ_HINT]
295irq_hint_handled:
296
297	sub r0, r0, 16
298
299	MOVR r1, _sw_isr_table
300	/* SW ISR table entries are 8-bytes wide for 32bit ISA and
301	 * 16-bytes wide for 64bit ISA */
302	ASLR r0, r0, (ARC_REGSHIFT + 1)
303	ADDR r0, r1, r0
304	/* ISR into r1 */
305	LDR r1, r0, ARC_REGSZ
306	jl_s.d [r1]
307	/* delay slot: ISR parameter into r0 */
308	LDR r0, r0
309
310#ifdef CONFIG_TRACING_ISR
311	bl sys_trace_isr_exit
312#endif
313
314#ifdef CONFIG_ARC_HAS_ACCL_REGS
315#ifndef CONFIG_64BIT
316	POPR r59
317#endif /* !CONFIG_64BIT */
318	POPR r58
319#endif
320
321	POPR r30
322	POPR r25
323
324	/* back from ISR, jump to exit stub */
325	POPR r3
326	j_s [r3]
327	nop_s
328