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