| 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 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 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 | |