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/processor.h> 18#include <asm/coprocessor.h> 19#include <asm/thread_info.h> 20#include <asm/uaccess.h> 21#include <asm/unistd.h> 22#include <asm/ptrace.h> 23#include <asm/current.h> 24#include <asm/pgtable.h> 25#include <asm/page.h> 26#include <asm/signal.h> 27#include <asm/tlbflush.h> 28 29/* 30 * Entry condition: 31 * 32 * a0: trashed, original value saved on stack (PT_AREG0) 33 * a1: a1 34 * a2: new stack pointer, original in DEPC 35 * a3: dispatch table 36 * depc: a2, original value saved on stack (PT_DEPC) 37 * excsave_1: a3 38 * 39 * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC 40 * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception 41 */ 42 43/* IO protection is currently unsupported. */ 44 45ENTRY(fast_io_protect) 46 wsr a0, EXCSAVE_1 47 movi a0, unrecoverable_exception 48 callx0 a0 49 50#if XTENSA_HAVE_COPROCESSORS 51 52/* 53 * Macros for lazy context switch. 54 */ 55 56#define SAVE_CP_REGS(x) \ 57 .align 4; \ 58 .Lsave_cp_regs_cp##x: \ 59 .if XTENSA_HAVE_COPROCESSOR(x); \ 60 xchal_cp##x##_store a2 a4 a5 a6 a7; \ 61 .endif; \ 62 jx a0 63 64#define SAVE_CP_REGS_TAB(x) \ 65 .if XTENSA_HAVE_COPROCESSOR(x); \ 66 .long .Lsave_cp_regs_cp##x - .Lsave_cp_regs_jump_table; \ 67 .else; \ 68 .long 0; \ 69 .endif; \ 70 .long THREAD_XTREGS_CP##x 71 72 73#define LOAD_CP_REGS(x) \ 74 .align 4; \ 75 .Lload_cp_regs_cp##x: \ 76 .if XTENSA_HAVE_COPROCESSOR(x); \ 77 xchal_cp##x##_load a2 a4 a5 a6 a7; \ 78 .endif; \ 79 jx a0 80 81#define LOAD_CP_REGS_TAB(x) \ 82 .if XTENSA_HAVE_COPROCESSOR(x); \ 83 .long .Lload_cp_regs_cp##x - .Lload_cp_regs_jump_table; \ 84 .else; \ 85 .long 0; \ 86 .endif; \ 87 .long THREAD_XTREGS_CP##x 88 89 SAVE_CP_REGS(0) 90 SAVE_CP_REGS(1) 91 SAVE_CP_REGS(2) 92 SAVE_CP_REGS(3) 93 SAVE_CP_REGS(4) 94 SAVE_CP_REGS(5) 95 SAVE_CP_REGS(6) 96 SAVE_CP_REGS(7) 97 98 LOAD_CP_REGS(0) 99 LOAD_CP_REGS(1) 100 LOAD_CP_REGS(2) 101 LOAD_CP_REGS(3) 102 LOAD_CP_REGS(4) 103 LOAD_CP_REGS(5) 104 LOAD_CP_REGS(6) 105 LOAD_CP_REGS(7) 106 107 .align 4 108.Lsave_cp_regs_jump_table: 109 SAVE_CP_REGS_TAB(0) 110 SAVE_CP_REGS_TAB(1) 111 SAVE_CP_REGS_TAB(2) 112 SAVE_CP_REGS_TAB(3) 113 SAVE_CP_REGS_TAB(4) 114 SAVE_CP_REGS_TAB(5) 115 SAVE_CP_REGS_TAB(6) 116 SAVE_CP_REGS_TAB(7) 117 118.Lload_cp_regs_jump_table: 119 LOAD_CP_REGS_TAB(0) 120 LOAD_CP_REGS_TAB(1) 121 LOAD_CP_REGS_TAB(2) 122 LOAD_CP_REGS_TAB(3) 123 LOAD_CP_REGS_TAB(4) 124 LOAD_CP_REGS_TAB(5) 125 LOAD_CP_REGS_TAB(6) 126 LOAD_CP_REGS_TAB(7) 127 128/* 129 * coprocessor_save(buffer, index) 130 * a2 a3 131 * coprocessor_load(buffer, index) 132 * a2 a3 133 * 134 * Save or load coprocessor registers for coprocessor 'index'. 135 * The register values are saved to or loaded from them 'buffer' address. 136 * 137 * Note that these functions don't update the coprocessor_owner information! 138 * 139 */ 140 141ENTRY(coprocessor_save) 142 entry a1, 32 143 s32i a0, a1, 0 144 movi a0, .Lsave_cp_regs_jump_table 145 addx8 a3, a3, a0 146 l32i a3, a3, 0 147 beqz a3, 1f 148 add a0, a0, a3 149 callx0 a0 1501: l32i a0, a1, 0 151 retw 152 153ENTRY(coprocessor_load) 154 entry a1, 32 155 s32i a0, a1, 0 156 movi a0, .Lload_cp_regs_jump_table 157 addx4 a3, a3, a0 158 l32i a3, a3, 0 159 beqz a3, 1f 160 add a0, a0, a3 161 callx0 a0 1621: l32i a0, a1, 0 163 retw 164 165/* 166 * coprocessor_flush(struct task_info*, index) 167 * a2 a3 168 * coprocessor_restore(struct task_info*, index) 169 * a2 a3 170 * 171 * Save or load coprocessor registers for coprocessor 'index'. 172 * The register values are saved to or loaded from the coprocessor area 173 * inside the task_info structure. 174 * 175 * Note that these functions don't update the coprocessor_owner information! 176 * 177 */ 178 179 180ENTRY(coprocessor_flush) 181 entry a1, 32 182 s32i a0, a1, 0 183 movi a0, .Lsave_cp_regs_jump_table 184 addx8 a3, a3, a0 185 l32i a4, a3, 4 186 l32i a3, a3, 0 187 add a2, a2, a4 188 beqz a3, 1f 189 add a0, a0, a3 190 callx0 a0 1911: l32i a0, a1, 0 192 retw 193 194ENTRY(coprocessor_restore) 195 entry a1, 32 196 s32i a0, a1, 0 197 movi a0, .Lload_cp_regs_jump_table 198 addx4 a3, a3, a0 199 l32i a4, a3, 4 200 l32i a3, a3, 0 201 add a2, a2, a4 202 beqz a3, 1f 203 add a0, a0, a3 204 callx0 a0 2051: l32i a0, a1, 0 206 retw 207 208/* 209 * Entry condition: 210 * 211 * a0: trashed, original value saved on stack (PT_AREG0) 212 * a1: a1 213 * a2: new stack pointer, original in DEPC 214 * a3: dispatch table 215 * depc: a2, original value saved on stack (PT_DEPC) 216 * excsave_1: a3 217 * 218 * PT_DEPC >= VALID_DOUBLE_EXCEPTION_ADDRESS: double exception, DEPC 219 * < VALID_DOUBLE_EXCEPTION_ADDRESS: regular exception 220 */ 221 222ENTRY(fast_coprocessor_double) 223 wsr a0, EXCSAVE_1 224 movi a0, unrecoverable_exception 225 callx0 a0 226 227 228ENTRY(fast_coprocessor) 229 230 /* Save remaining registers a1-a3 and SAR */ 231 232 xsr a3, EXCSAVE_1 233 s32i a3, a2, PT_AREG3 234 rsr a3, SAR 235 s32i a1, a2, PT_AREG1 236 s32i a3, a2, PT_SAR 237 mov a1, a2 238 rsr a2, DEPC 239 s32i a2, a1, PT_AREG2 240 241 /* 242 * The hal macros require up to 4 temporary registers. We use a3..a6. 243 */ 244 245 s32i a4, a1, PT_AREG4 246 s32i a5, a1, PT_AREG5 247 s32i a6, a1, PT_AREG6 248 249 /* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */ 250 251 rsr a3, EXCCAUSE 252 addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED 253 254 /* Set corresponding CPENABLE bit -> (sar:cp-index, a3: 1<<cp-index)*/ 255 256 ssl a3 # SAR: 32 - coprocessor_number 257 movi a2, 1 258 rsr a0, CPENABLE 259 sll a2, a2 260 or a0, a0, a2 261 wsr a0, CPENABLE 262 rsync 263 264 /* Retrieve previous owner. (a3 still holds CP number) */ 265 266 movi a0, coprocessor_owner # list of owners 267 addx4 a0, a3, a0 # entry for CP 268 l32i a4, a0, 0 269 270 beqz a4, 1f # skip 'save' if no previous owner 271 272 /* Disable coprocessor for previous owner. (a2 = 1 << CP number) */ 273 274 l32i a5, a4, THREAD_CPENABLE 275 xor a5, a5, a2 # (1 << cp-id) still in a2 276 s32i a5, a4, THREAD_CPENABLE 277 278 /* 279 * Get context save area and 'call' save routine. 280 * (a4 still holds previous owner (thread_info), a3 CP number) 281 */ 282 283 movi a5, .Lsave_cp_regs_jump_table 284 movi a0, 2f # a0: 'return' address 285 addx8 a3, a3, a5 # a3: coprocessor number 286 l32i a2, a3, 4 # a2: xtregs offset 287 l32i a3, a3, 0 # a3: jump offset 288 add a2, a2, a4 289 add a4, a3, a5 # a4: address of save routine 290 jx a4 291 292 /* Note that only a0 and a1 were preserved. */ 293 2942: rsr a3, EXCCAUSE 295 addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED 296 movi a0, coprocessor_owner 297 addx4 a0, a3, a0 298 299 /* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */ 300 3011: GET_THREAD_INFO (a4, a1) 302 s32i a4, a0, 0 303 304 /* Get context save area and 'call' load routine. */ 305 306 movi a5, .Lload_cp_regs_jump_table 307 movi a0, 1f 308 addx8 a3, a3, a5 309 l32i a2, a3, 4 # a2: xtregs offset 310 l32i a3, a3, 0 # a3: jump offset 311 add a2, a2, a4 312 add a4, a3, a5 313 jx a4 314 315 /* Restore all registers and return from exception handler. */ 316 3171: l32i a6, a1, PT_AREG6 318 l32i a5, a1, PT_AREG5 319 l32i a4, a1, PT_AREG4 320 321 l32i a0, a1, PT_SAR 322 l32i a3, a1, PT_AREG3 323 l32i a2, a1, PT_AREG2 324 wsr a0, SAR 325 l32i a0, a1, PT_AREG0 326 l32i a1, a1, PT_AREG1 327 328 rfe 329 330 .data 331ENTRY(coprocessor_owner) 332 .fill XCHAL_CP_MAX, 4, 0 333 334#endif /* XTENSA_HAVE_COPROCESSORS */ 335 336