1/*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8#include <xtensa/coreasm.h>
9#include <xtensa/corebits.h>
10#include <xtensa/config/system.h>
11#include "freertos/xtensa_context.h"
12#include "sdkconfig.h"
13#include "soc/soc.h"
14
15#if CONFIG_BTDM_CTRL_HLI
16
17/* Interrupt stack size, for C code.
18 * TODO: reduce and make configurable.
19 */
20#define L4_INTR_STACK_SIZE  4096
21
22/* Save area for the CPU state:
23 * - 64 words for the general purpose registers
24 * - 7 words for some of the special registers:
25 *   - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
26 *   - SAR, LBEG, LEND, LCOUNT — since the C code might use these
27 *   - EPC1 — since the C code might cause window overflow exceptions
28 * This is not laid out as standard exception frame structure
29 * for simplicity of the save/restore code.
30 */
31#define REG_FILE_SIZE         (64 * 4)
32#define SPECREG_OFFSET        REG_FILE_SIZE
33#define SPECREG_SIZE          (7 * 4)
34#define REG_SAVE_AREA_SIZE    (SPECREG_OFFSET + SPECREG_SIZE)
35
36    .data
37_l4_intr_stack:
38    .space      L4_INTR_STACK_SIZE
39_l4_save_ctx:
40    .space      REG_SAVE_AREA_SIZE
41
42    .section .iram1,"ax"
43    .global     xt_highint4
44    .type       xt_highint4,@function
45    .align      4
46
47xt_highint4:
48
49#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
50    /*
51    Here, Timer2 is used to count a little time(50us).
52    The subsequent dram0 write operation is blocked due to live lock, which will
53    cause timer2 to timeout and trigger a level 5 interrupt.
54    */
55    rsr.ccount  a0
56    addmi   a0, a0, (CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ*50)
57    wsr     a0, CCOMPARE2
58
59    /* Enable Timer 2 interrupt */
60    rsr     a0, INTENABLE
61    extui   a0, a0, 16, 1
62    bnez    a0, 1f
63    movi    a0, 0
64    xsr     a0, INTENABLE     /* disable all interrupts */
65    /* And a0 with (1 << 16) for Timer 2 interrupt mask */
66    addmi   a0, a0, (1<<14)
67    addmi   a0, a0, (1<<14)
68    addmi   a0, a0, (1<<14)
69    addmi   a0, a0, (1<<14)
70    wsr     a0, INTENABLE    /* Enable Timer 2 */
711:
72#endif
73
74    movi    a0, _l4_save_ctx
75    /* save 4 lower registers */
76    s32i    a1, a0, 4
77    s32i    a2, a0, 8
78    s32i    a3, a0, 12
79    rsr     a2, EXCSAVE_4  /* holds the value of a0 */
80    s32i    a2, a0, 0
81
82    /* Save special registers */
83    addi    a0, a0, SPECREG_OFFSET
84    rsr     a2, WINDOWBASE
85    s32i    a2, a0, 0
86    rsr     a2, WINDOWSTART
87    s32i    a2, a0, 4
88    rsr     a2, SAR
89    s32i    a2, a0, 8
90    rsr     a2, LBEG
91    s32i    a2, a0, 12
92    rsr     a2, LEND
93    s32i    a2, a0, 16
94    rsr     a2, LCOUNT
95    s32i    a2, a0, 20
96    rsr     a2, EPC1
97    s32i    a2, a0, 24
98
99#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
100    movi    a0, 0
101    xsr     a0, INTENABLE     /* disable all interrupts */
102    movi    a2, ~(1<<16)
103    and     a0, a2, a0
104    wsr     a0, INTENABLE
105#endif
106
107    /* disable exception mode, window overflow */
108    movi    a0, PS_INTLEVEL(5) | PS_EXCM
109    wsr     a0, PS
110    rsync
111
112    /* Save the remaining physical registers.
113     * 4 registers are already saved, which leaves 60 registers to save.
114     * (FIXME: consider the case when the CPU is configured with physical 32 registers)
115     * These 60 registers are saved in 5 iterations, 12 registers at a time.
116     */
117    movi    a1, 5
118    movi    a3, _l4_save_ctx + 4 * 4
119
120    /* This is repeated 5 times, each time the window is shifted by 12 registers.
121     * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
122     */
1231:
124    s32i    a4, a3, 0
125    s32i    a5, a3, 4
126    s32i    a6, a3, 8
127    s32i    a7, a3, 12
128    s32i    a8, a3, 16
129    s32i    a9, a3, 20
130    s32i    a10, a3, 24
131    s32i    a11, a3, 28
132    s32i    a12, a3, 32
133    s32i    a13, a3, 36
134    s32i    a14, a3, 40
135    s32i    a15, a3, 44
136
137    /* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
138     * Copy a0-a3 to a12-15 to still have access to these values.
139     * At the same time we can decrement the counter and adjust the save area pointer
140     */
141
142    /* a0 is constant (_l4_save_ctx), no need to copy */
143    addi    a13, a1, -1  /* copy and decrement the downcounter */
144    /* a2 is scratch so no need to copy */
145    addi    a15, a3, 48  /* copy and adjust the save area pointer */
146    beqz    a13, 2f      /* have saved all registers ? */
147    rotw    3            /* rotate the window and go back */
148    j       1b
149
150    /* the loop is complete */
1512:
152    rotw 4      /* this brings us back to the original window */
153    /* a0 still points to _l4_save_ctx */
154
155    /* Can clear WINDOWSTART now, all registers are saved */
156    rsr     a2, WINDOWBASE
157    /* WINDOWSTART = (1 << WINDOWBASE) */
158    movi    a3, 1
159    ssl     a2
160    sll     a3, a3
161    wsr     a3, WINDOWSTART
162
163_highint4_stack_switch:
164    movi    a0, 0
165    movi    sp, _l4_intr_stack + L4_INTR_STACK_SIZE - 16
166    s32e    a0, sp, -12         /* For GDB: set null SP */
167    s32e    a0, sp, -16         /* For GDB: set null PC */
168    movi    a0, _highint4_stack_switch     /* For GDB: cosmetics, for the frame where stack switch happened */
169
170    /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
171    movi    a6, PS_INTLEVEL(4) | PS_UM | PS_WOE
172    wsr     a6, PS
173    rsync
174
175    /* Call C handler */
176    mov     a6, sp
177    call4   hli_c_handler
178
179    l32e    sp, sp, -12                     /* switch back to the original stack */
180
181    /* Done with C handler; re-enable exception mode, disabling window overflow */
182    movi    a2, PS_INTLEVEL(5) | PS_EXCM    /* TOCHECK */
183    wsr     a2, PS
184    rsync
185
186    /* Restore the special registers.
187     * WINDOWSTART will be restored near the end.
188     */
189    movi    a0, _l4_save_ctx + SPECREG_OFFSET
190    l32i    a2, a0, 8
191    wsr     a2, SAR
192    l32i    a2, a0, 12
193    wsr     a2, LBEG
194    l32i    a2, a0, 16
195    wsr     a2, LEND
196    l32i    a2, a0, 20
197    wsr     a2, LCOUNT
198    l32i    a2, a0, 24
199    wsr     a2, EPC1
200
201    /* Restoring the physical registers.
202     * This is the reverse to the saving process above.
203     */
204
205    /* Rotate back to the final window, then start loading 12 registers at a time,
206     * in 5 iterations.
207     * Again, a1 is the downcounter and a3 is the save area pointer.
208     * After each rotation, a1 and a3 are copied from a13 and a15.
209     * To simplify the loop, we put the initial values into a13 and a15.
210     */
211    rotw     -4
212    movi    a15, _l4_save_ctx + 64 * 4  /* point to the end of the save area */
213    movi    a13, 5
214
2151:
216    /* Copy a1 and a3 from their previous location,
217     * at the same time decrementing and adjusting the save area pointer.
218     */
219    addi    a1, a13, -1
220    addi    a3, a15, -48
221
222    /* Load 12 registers */
223    l32i    a4, a3, 0
224    l32i    a5, a3, 4
225    l32i    a6, a3, 8
226    l32i    a7, a3, 12
227    l32i    a8, a3, 16
228    l32i    a9, a3, 20
229    l32i    a10, a3, 24
230    l32i    a11, a3, 28                                /* ensure PS and EPC written */
231    l32i    a12, a3, 32
232    l32i    a13, a3, 36
233    l32i    a14, a3, 40
234    l32i    a15, a3, 44
235
236    /* Done with the loop? */
237    beqz    a1, 2f
238    /* If no, rotate the window and repeat */
239    rotw    -3
240    j       1b
241
2422:
243    /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
244     * to be restored. Also need to restore WINDOWSTART, since all the general
245     * registers are now in place.
246     */
247    movi    a0, _l4_save_ctx
248
249    l32i    a2, a0, SPECREG_OFFSET + 4
250    wsr     a2, WINDOWSTART
251
252    l32i    a1, a0, 4
253    l32i    a2, a0, 8
254    l32i    a3, a0, 12
255    rsr     a0, EXCSAVE_4  /* holds the value of a0 before the interrupt handler */
256
257    /* Return from the interrupt, restoring PS from EPS_4 */
258    rfi     4
259
260#endif /* CONFIG_BTDM_CTRL_HLI */
261
262/* The linker has no reason to link in this file; all symbols it exports are already defined
263   (weakly!) in the default int handler. Define a symbol here so we can use it to have the
264   linker inspect this anyway. */
265
266    .global ld_include_hli_vectors_bt
267ld_include_hli_vectors_bt:
268