1/*
2 * Copyright (c) 2019-2020 Cobham Gaisler AB
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include <zephyr/toolchain.h>
8#include <zephyr/linker/sections.h>
9#include <offsets_short.h>
10#include <zephyr/arch/sparc/sparc.h>
11
12GTEXT(z_sparc_arch_switch)
13GTEXT(z_sparc_context_switch)
14GTEXT(z_thread_entry_wrapper)
15
16/*
17 * The routine z_sparc_context_switch() is called from arch_switch(), or from
18 * the interrupt trap handler in case of preemption. The subtraction to get the
19 * "old" thread from "switched_from" has already been performed and the "old"
20 * thread is now in register %o1. We can address old->switch_handle in assembly
21 * as: [%o1 + ___thread_t_switch_handle_OFFSET].
22 *
23 * The switch_handle is written in z_sparc_context_switch() after the old
24 * context has been saved.
25 *
26 * This is a leaf function, so only out registers
27 * can be used without saving their context first.
28 *
29 * o0: new thread to restore
30 * o1: old thread to save
31 */
32SECTION_FUNC(TEXT, z_sparc_context_switch)
33	mov	%y, %o4
34	st	%o4, [%o1 + _thread_offset_to_y]
35	std	%l0, [%o1 + _thread_offset_to_l0_and_l1]
36	std	%l2, [%o1 + _thread_offset_to_l2]
37	std	%l4, [%o1 + _thread_offset_to_l4]
38	std	%l6, [%o1 + _thread_offset_to_l6]
39	std	%i0, [%o1 + _thread_offset_to_i0]
40	std	%i2, [%o1 + _thread_offset_to_i2]
41	std	%i4, [%o1 + _thread_offset_to_i4]
42	std	%i6, [%o1 + _thread_offset_to_i6]
43
44	std	%o6, [%o1 + _thread_offset_to_o6]
45
46	rd	%psr, %o4
47	st	%o4, [%o1 + _thread_offset_to_psr]
48
49	and	%o4, PSR_CWP, %g3       /* %g3 = CWP */
50	andn	%o4, PSR_ET, %g1        /* %g1 = psr with traps disabled */
51	wr	%g1, %psr               /* disable traps */
52	nop
53	nop
54	nop
55
56	rd	%wim, %g2               /* %g2 = wim */
57	mov	1, %g4
58	sll	%g4, %g3, %g4           /* %g4 = wim mask for CW invalid */
59
60.Lsave_frame_loop:
61	sll	%g4, 1, %g5             /* rotate wim left by 1 */
62	srl	%g4, (CONFIG_SPARC_NWIN-1), %g4
63	or	%g4, %g5, %g4           /* %g4 = wim if we do one restore */
64
65	/* if restore would not underflow, continue */
66	andcc	%g4, %g2, %g0           /* window to flush? */
67	bnz    .Ldone_flushing          /* continue */
68	 nop
69	restore                         /* go one window back */
70
71	/* essentially the same as window overflow */
72	/* sp still points to task stack */
73	std	%l0, [%sp + 0x00]
74	std	%l2, [%sp + 0x08]
75	std	%l4, [%sp + 0x10]
76	std	%l6, [%sp + 0x18]
77	std	%i0, [%sp + 0x20]
78	std	%i2, [%sp + 0x28]
79	std	%i4, [%sp + 0x30]
80	std	%i6, [%sp + 0x38]
81	ba	.Lsave_frame_loop
82	 nop
83
84.Ldone_flushing:
85	/*
86	 * "wrpsr" is a delayed write instruction so wait three instructions
87	 * after the write before using non-global registers or instructions
88	 * affecting the CWP.
89	 */
90	wr	%g1, %psr               /* restore cwp */
91	nop
92	nop
93	nop
94	add	%g3, 1, %g2             /* calculate desired wim */
95	cmp	%g2, (CONFIG_SPARC_NWIN-1) /* check if wim is in range */
96	bg,a	.Lwim_overflow
97	 mov	0, %g2
98
99.Lwim_overflow:
100
101	mov	1, %g4
102	sll	%g4, %g2, %g4           /* %g4 = new wim */
103	wr	%g4, %wim
104	nop
105	nop
106	nop
107
108	/*
109	 * We have finished saving the "old" context and are also back in the
110	 * register window for which z_sparc_context_switch() was called.
111	 *
112	 * Now write the old thread into switch handle.
113	 * "old->switch_handle = old".
114	 */
115	st	%o1, [%o1 + ___thread_t_switch_handle_OFFSET]
116
117	ldd	[%o0 + _thread_offset_to_y], %o4
118	mov	%o4, %y
119
120	/* restore local registers */
121	ldd	[%o0 + _thread_offset_to_l0_and_l1], %l0
122	ldd	[%o0 + _thread_offset_to_l2], %l2
123	ldd	[%o0 + _thread_offset_to_l4], %l4
124	ldd	[%o0 + _thread_offset_to_l6], %l6
125
126	/* restore input registers */
127	ldd	[%o0 + _thread_offset_to_i0], %i0
128	ldd	[%o0 + _thread_offset_to_i2], %i2
129	ldd	[%o0 + _thread_offset_to_i4], %i4
130	ldd	[%o0 + _thread_offset_to_i6], %i6
131
132	/* restore output registers */
133	ldd	[%o0 + _thread_offset_to_o6], %o6
134#ifdef CONFIG_THREAD_LOCAL_STORAGE
135	ld	[%o0 + _thread_offset_to_tls], %g7
136#endif
137
138	ld	[%o0 + _thread_offset_to_psr], %g1  /* %g1 = new thread psr */
139
140	andn	%g1, PSR_CWP, %g1       /* psr without cwp */
141	or	%g1, %g3, %g1           /* psr with new cwp */
142	wr	%g1, %psr               /* restore status register and ET */
143	nop
144	nop
145	nop
146
147	/* jump into thread */
148	jmp	%o7 + 8
149	 nop
150
151SECTION_FUNC(TEXT, z_thread_entry_wrapper)
152	mov	%g0, %o7
153	mov	%i0, %o0
154	mov	%i1, %o1
155	mov	%i2, %o2
156	mov	%i3, %o3
157	call	z_thread_entry
158	 nop
159