1 /* 2 * arch/xtensa/kernel/coprocessor.S 3 * 4 * Xtensa processor configuration-specific table of coprocessor and 5 * other custom register layout information. 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file "COPYING" in the main directory of this archive 9 * for more details. 10 * 11 * Copyright (C) 2003 - 2007 Tensilica Inc. 12 */ 13 14 15 #include <linux/linkage.h> 16 #include <asm/asm-offsets.h> 17 #include <asm/asmmacro.h> 18 #include <asm/coprocessor.h> 19 #include <asm/current.h> 20 #include <asm/regs.h> 21 22 /* 23 * Rules for coprocessor state manipulation on SMP: 24 * 25 * - a task may have live coprocessors only on one CPU. 26 * 27 * - whether coprocessor context of task T is live on some CPU is 28 * denoted by T's thread_info->cpenable. 29 * 30 * - non-zero thread_info->cpenable means that thread_info->cp_owner_cpu 31 * is valid in the T's thread_info. Zero thread_info->cpenable means that 32 * coprocessor context is valid in the T's thread_info. 33 * 34 * - if a coprocessor context of task T is live on CPU X, only CPU X changes 35 * T's thread_info->cpenable, cp_owner_cpu and coprocessor save area. 36 * This is done by making sure that for the task T with live coprocessor 37 * on CPU X cpenable SR is 0 when T runs on any other CPU Y. 38 * When fast_coprocessor exception is taken on CPU Y it goes to the 39 * C-level do_coprocessor that uses IPI to make CPU X flush T's coprocessors. 40 */ 41 42 #if XTENSA_HAVE_COPROCESSORS 43 44 /* 45 * Macros for lazy context switch. 46 */ 47 48 #define SAVE_CP_REGS(x) \ 49 .if XTENSA_HAVE_COPROCESSOR(x); \ 50 .align 4; \ 51 .Lsave_cp_regs_cp##x: \ 52 xchal_cp##x##_store a2 a3 a4 a5 a6; \ 53 ret; \ 54 .endif 55 56 #define LOAD_CP_REGS(x) \ 57 .if XTENSA_HAVE_COPROCESSOR(x); \ 58 .align 4; \ 59 .Lload_cp_regs_cp##x: \ 60 xchal_cp##x##_load a2 a3 a4 a5 a6; \ 61 ret; \ 62 .endif 63 64 #define CP_REGS_TAB(x) \ 65 .if XTENSA_HAVE_COPROCESSOR(x); \ 66 .long .Lsave_cp_regs_cp##x; \ 67 .long .Lload_cp_regs_cp##x; \ 68 .else; \ 69 .long 0, 0; \ 70 .endif; \ 71 .long THREAD_XTREGS_CP##x 72 73 #define CP_REGS_TAB_SAVE 0 74 #define CP_REGS_TAB_LOAD 4 75 #define CP_REGS_TAB_OFFSET 8 76 77 __XTENSA_HANDLER 78 79 SAVE_CP_REGS(0) 80 SAVE_CP_REGS(1) 81 SAVE_CP_REGS(2) 82 SAVE_CP_REGS(3) 83 SAVE_CP_REGS(4) 84 SAVE_CP_REGS(5) 85 SAVE_CP_REGS(6) 86 SAVE_CP_REGS(7) 87 88 LOAD_CP_REGS(0) 89 LOAD_CP_REGS(1) 90 LOAD_CP_REGS(2) 91 LOAD_CP_REGS(3) 92 LOAD_CP_REGS(4) 93 LOAD_CP_REGS(5) 94 LOAD_CP_REGS(6) 95 LOAD_CP_REGS(7) 96 97 .align 4 98 .Lcp_regs_jump_table: 99 CP_REGS_TAB(0) 100 CP_REGS_TAB(1) 101 CP_REGS_TAB(2) 102 CP_REGS_TAB(3) 103 CP_REGS_TAB(4) 104 CP_REGS_TAB(5) 105 CP_REGS_TAB(6) 106 CP_REGS_TAB(7) 107 108 /* 109 * Entry condition: 110 * 111 * a0: trashed, original value saved on stack (PT_AREG0) 112 * a1: a1 113 * a2: new stack pointer, original in DEPC 114 * a3: a3 115 * depc: a2, original value saved on stack (PT_DEPC) 116 * excsave_1: dispatch table 117 * 118 * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC 119 * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception 120 */ 121 122 ENTRY(fast_coprocessor) 123 124 s32i a3, a2, PT_AREG3 125 126 #ifdef CONFIG_SMP 127 /* 128 * Check if any coprocessor context is live on another CPU 129 * and if so go through the C-level coprocessor exception handler 130 * to flush it to memory. 131 */ 132 GET_THREAD_INFO (a0, a2) 133 l32i a3, a0, THREAD_CPENABLE 134 beqz a3, .Lload_local 135 136 /* 137 * Pairs with smp_wmb in local_coprocessor_release_all 138 * and with both memws below. 139 */ 140 memw 141 l32i a3, a0, THREAD_CPU 142 l32i a0, a0, THREAD_CP_OWNER_CPU 143 beq a0, a3, .Lload_local 144 145 rsr a0, ps 146 l32i a3, a2, PT_AREG3 147 bbci.l a0, PS_UM_BIT, 1f 148 call0 user_exception 149 1: call0 kernel_exception 150 #endif 151 152 /* Save remaining registers a1-a3 and SAR */ 153 154 .Lload_local: 155 rsr a3, sar 156 s32i a1, a2, PT_AREG1 157 s32i a3, a2, PT_SAR 158 mov a1, a2 159 rsr a2, depc 160 s32i a2, a1, PT_AREG2 161 162 /* The hal macros require up to 4 temporary registers. We use a3..a6. */ 163 164 s32i a4, a1, PT_AREG4 165 s32i a5, a1, PT_AREG5 166 s32i a6, a1, PT_AREG6 167 s32i a7, a1, PT_AREG7 168 s32i a8, a1, PT_AREG8 169 s32i a9, a1, PT_AREG9 170 s32i a10, a1, PT_AREG10 171 172 /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */ 173 174 rsr a3, exccause 175 addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED 176 177 /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/ 178 179 ssl a3 # SAR: 32 - coprocessor_number 180 movi a2, 1 181 rsr a0, cpenable 182 sll a2, a2 183 or a0, a0, a2 184 wsr a0, cpenable 185 rsync 186 187 /* Get coprocessor save/load table entry (a7). */ 188 189 movi a7, .Lcp_regs_jump_table 190 addx8 a7, a3, a7 191 addx4 a7, a3, a7 192 193 /* Retrieve previous owner (a8). */ 194 195 rsr a0, excsave1 # exc_table 196 addx4 a0, a3, a0 # entry for CP 197 l32i a8, a0, EXC_TABLE_COPROCESSOR_OWNER 198 199 /* Set new owner (a9). */ 200 201 GET_THREAD_INFO (a9, a1) 202 l32i a4, a9, THREAD_CPU 203 s32i a9, a0, EXC_TABLE_COPROCESSOR_OWNER 204 s32i a4, a9, THREAD_CP_OWNER_CPU 205 206 /* 207 * Enable coprocessor for the new owner. (a2 = 1 << CP number) 208 * This can be done before loading context into the coprocessor. 209 */ 210 l32i a4, a9, THREAD_CPENABLE 211 or a4, a4, a2 212 213 /* 214 * Make sure THREAD_CP_OWNER_CPU is in memory before updating 215 * THREAD_CPENABLE 216 */ 217 memw # (2) 218 s32i a4, a9, THREAD_CPENABLE 219 220 beqz a8, 1f # skip 'save' if no previous owner 221 222 /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */ 223 224 l32i a10, a8, THREAD_CPENABLE 225 xor a10, a10, a2 226 227 /* Get context save area and call save routine. */ 228 229 l32i a2, a7, CP_REGS_TAB_OFFSET 230 l32i a3, a7, CP_REGS_TAB_SAVE 231 add a2, a2, a8 232 callx0 a3 233 234 /* 235 * Make sure coprocessor context and THREAD_CP_OWNER_CPU are in memory 236 * before updating THREAD_CPENABLE 237 */ 238 memw # (3) 239 s32i a10, a8, THREAD_CPENABLE 240 1: 241 /* Get context save area and call load routine. */ 242 243 l32i a2, a7, CP_REGS_TAB_OFFSET 244 l32i a3, a7, CP_REGS_TAB_LOAD 245 add a2, a2, a9 246 callx0 a3 247 248 /* Restore all registers and return from exception handler. */ 249 250 l32i a10, a1, PT_AREG10 251 l32i a9, a1, PT_AREG9 252 l32i a8, a1, PT_AREG8 253 l32i a7, a1, PT_AREG7 254 l32i a6, a1, PT_AREG6 255 l32i a5, a1, PT_AREG5 256 l32i a4, a1, PT_AREG4 257 258 l32i a0, a1, PT_SAR 259 l32i a3, a1, PT_AREG3 260 l32i a2, a1, PT_AREG2 261 wsr a0, sar 262 l32i a0, a1, PT_AREG0 263 l32i a1, a1, PT_AREG1 264 265 rfe 266 267 ENDPROC(fast_coprocessor) 268 269 .text 270 271 /* 272 * coprocessor_flush(struct thread_info*, index) 273 * a2 a3 274 * 275 * Save coprocessor registers for coprocessor 'index'. 276 * The register values are saved to or loaded from the coprocessor area 277 * inside the task_info structure. 278 * 279 * Note that this function doesn't update the coprocessor_owner information! 280 * 281 */ 282 283 ENTRY(coprocessor_flush) 284 285 abi_entry_default 286 287 movi a4, .Lcp_regs_jump_table 288 addx8 a4, a3, a4 289 addx4 a3, a3, a4 290 l32i a4, a3, CP_REGS_TAB_SAVE 291 beqz a4, 1f 292 l32i a3, a3, CP_REGS_TAB_OFFSET 293 add a2, a2, a3 294 mov a7, a0 295 callx0 a4 296 mov a0, a7 297 1: 298 abi_ret_default 299 300 ENDPROC(coprocessor_flush) 301 302 #endif /* XTENSA_HAVE_COPROCESSORS */
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.