Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | /* SPDX-License-Identifier: GPL-2.0-only */ /* * linux/arch/arm/kernel/iwmmxt.S * * XScale iWMMXt (Concan) context switching and handling * * Initial code: * Copyright (c) 2003, Intel Corporation * * Full lazy switching support, optimizations and more, by Nicolas Pitre * Copyright (c) 2003-2004, MontaVista Software, Inc. */ #include <linux/linkage.h> #include <asm/ptrace.h> #include <asm/thread_info.h> #include <asm/asm-offsets.h> #include <asm/assembler.h> #include "iwmmxt.h" #if defined(CONFIG_CPU_PJ4) || defined(CONFIG_CPU_PJ4B) #define PJ4(code...) code #define XSC(code...) #elif defined(CONFIG_CPU_MOHAWK) || \ defined(CONFIG_CPU_XSC3) || \ defined(CONFIG_CPU_XSCALE) #define PJ4(code...) #define XSC(code...) code #else #error "Unsupported iWMMXt architecture" #endif #define MMX_WR0 (0x00) #define MMX_WR1 (0x08) #define MMX_WR2 (0x10) #define MMX_WR3 (0x18) #define MMX_WR4 (0x20) #define MMX_WR5 (0x28) #define MMX_WR6 (0x30) #define MMX_WR7 (0x38) #define MMX_WR8 (0x40) #define MMX_WR9 (0x48) #define MMX_WR10 (0x50) #define MMX_WR11 (0x58) #define MMX_WR12 (0x60) #define MMX_WR13 (0x68) #define MMX_WR14 (0x70) #define MMX_WR15 (0x78) #define MMX_WCSSF (0x80) #define MMX_WCASF (0x84) #define MMX_WCGR0 (0x88) #define MMX_WCGR1 (0x8C) #define MMX_WCGR2 (0x90) #define MMX_WCGR3 (0x94) #define MMX_SIZE (0x98) .text .arm /* * Lazy switching of Concan coprocessor context * * r10 = struct thread_info pointer * r9 = ret_from_exception * lr = undefined instr exit * * called from prefetch exception handler with interrupts enabled */ ENTRY(iwmmxt_task_enable) inc_preempt_count r10, r3 XSC(mrc p15, 0, r2, c15, c1, 0) PJ4(mrc p15, 0, r2, c1, c0, 2) @ CP0 and CP1 accessible? XSC(tst r2, #0x3) PJ4(tst r2, #0xf) bne 4f @ if so no business here @ enable access to CP0 and CP1 XSC(orr r2, r2, #0x3) XSC(mcr p15, 0, r2, c15, c1, 0) PJ4(orr r2, r2, #0xf) PJ4(mcr p15, 0, r2, c1, c0, 2) ldr r3, =concan_owner add r0, r10, #TI_IWMMXT_STATE @ get task Concan save area ldr r2, [sp, #60] @ current task pc value ldr r1, [r3] @ get current Concan owner str r0, [r3] @ this task now owns Concan regs sub r2, r2, #4 @ adjust pc back str r2, [sp, #60] mrc p15, 0, r2, c2, c0, 0 mov r2, r2 @ cpwait bl concan_save #ifdef CONFIG_PREEMPT_COUNT get_thread_info r10 #endif 4: dec_preempt_count r10, r3 ret r9 @ normal exit from exception concan_save: teq r1, #0 @ test for last ownership beq concan_load @ no owner, skip save tmrc r2, wCon @ CUP? wCx tst r2, #0x1 beq 1f concan_dump: wstrw wCSSF, r1, MMX_WCSSF wstrw wCASF, r1, MMX_WCASF wstrw wCGR0, r1, MMX_WCGR0 wstrw wCGR1, r1, MMX_WCGR1 wstrw wCGR2, r1, MMX_WCGR2 wstrw wCGR3, r1, MMX_WCGR3 1: @ MUP? wRn tst r2, #0x2 beq 2f wstrd wR0, r1, MMX_WR0 wstrd wR1, r1, MMX_WR1 wstrd wR2, r1, MMX_WR2 wstrd wR3, r1, MMX_WR3 wstrd wR4, r1, MMX_WR4 wstrd wR5, r1, MMX_WR5 wstrd wR6, r1, MMX_WR6 wstrd wR7, r1, MMX_WR7 wstrd wR8, r1, MMX_WR8 wstrd wR9, r1, MMX_WR9 wstrd wR10, r1, MMX_WR10 wstrd wR11, r1, MMX_WR11 wstrd wR12, r1, MMX_WR12 wstrd wR13, r1, MMX_WR13 wstrd wR14, r1, MMX_WR14 wstrd wR15, r1, MMX_WR15 2: teq r0, #0 @ anything to load? reteq lr @ if not, return concan_load: @ Load wRn wldrd wR0, r0, MMX_WR0 wldrd wR1, r0, MMX_WR1 wldrd wR2, r0, MMX_WR2 wldrd wR3, r0, MMX_WR3 wldrd wR4, r0, MMX_WR4 wldrd wR5, r0, MMX_WR5 wldrd wR6, r0, MMX_WR6 wldrd wR7, r0, MMX_WR7 wldrd wR8, r0, MMX_WR8 wldrd wR9, r0, MMX_WR9 wldrd wR10, r0, MMX_WR10 wldrd wR11, r0, MMX_WR11 wldrd wR12, r0, MMX_WR12 wldrd wR13, r0, MMX_WR13 wldrd wR14, r0, MMX_WR14 wldrd wR15, r0, MMX_WR15 @ Load wCx wldrw wCSSF, r0, MMX_WCSSF wldrw wCASF, r0, MMX_WCASF wldrw wCGR0, r0, MMX_WCGR0 wldrw wCGR1, r0, MMX_WCGR1 wldrw wCGR2, r0, MMX_WCGR2 wldrw wCGR3, r0, MMX_WCGR3 @ clear CUP/MUP (only if r1 != 0) teq r1, #0 mov r2, #0 reteq lr tmcr wCon, r2 ret lr ENDPROC(iwmmxt_task_enable) /* * Back up Concan regs to save area and disable access to them * (mainly for gdb or sleep mode usage) * * r0 = struct thread_info pointer of target task or NULL for any */ ENTRY(iwmmxt_task_disable) stmfd sp!, {r4, lr} mrs ip, cpsr orr r2, ip, #PSR_I_BIT @ disable interrupts msr cpsr_c, r2 ldr r3, =concan_owner add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area ldr r1, [r3] @ get current Concan owner teq r1, #0 @ any current owner? beq 1f @ no: quit teq r0, #0 @ any owner? teqne r1, r2 @ or specified one? bne 1f @ no: quit @ enable access to CP0 and CP1 XSC(mrc p15, 0, r4, c15, c1, 0) XSC(orr r4, r4, #0x3) XSC(mcr p15, 0, r4, c15, c1, 0) PJ4(mrc p15, 0, r4, c1, c0, 2) PJ4(orr r4, r4, #0xf) PJ4(mcr p15, 0, r4, c1, c0, 2) mov r0, #0 @ nothing to load str r0, [r3] @ no more current owner mrc p15, 0, r2, c2, c0, 0 mov r2, r2 @ cpwait bl concan_save @ disable access to CP0 and CP1 XSC(bic r4, r4, #0x3) XSC(mcr p15, 0, r4, c15, c1, 0) PJ4(bic r4, r4, #0xf) PJ4(mcr p15, 0, r4, c1, c0, 2) mrc p15, 0, r2, c2, c0, 0 mov r2, r2 @ cpwait 1: msr cpsr_c, ip @ restore interrupt mode ldmfd sp!, {r4, pc} ENDPROC(iwmmxt_task_disable) /* * Copy Concan state to given memory address * * r0 = struct thread_info pointer of target task * r1 = memory address where to store Concan state * * this is called mainly in the creation of signal stack frames */ ENTRY(iwmmxt_task_copy) mrs ip, cpsr orr r2, ip, #PSR_I_BIT @ disable interrupts msr cpsr_c, r2 ldr r3, =concan_owner add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area ldr r3, [r3] @ get current Concan owner teq r2, r3 @ does this task own it... beq 1f @ current Concan values are in the task save area msr cpsr_c, ip @ restore interrupt mode mov r0, r1 mov r1, r2 mov r2, #MMX_SIZE b memcpy 1: @ this task owns Concan regs -- grab a copy from there mov r0, #0 @ nothing to load mov r2, #3 @ save all regs mov r3, lr @ preserve return address bl concan_dump msr cpsr_c, ip @ restore interrupt mode ret r3 ENDPROC(iwmmxt_task_copy) /* * Restore Concan state from given memory address * * r0 = struct thread_info pointer of target task * r1 = memory address where to get Concan state from * * this is used to restore Concan state when unwinding a signal stack frame */ ENTRY(iwmmxt_task_restore) mrs ip, cpsr orr r2, ip, #PSR_I_BIT @ disable interrupts msr cpsr_c, r2 ldr r3, =concan_owner add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area ldr r3, [r3] @ get current Concan owner bic r2, r2, #0x7 @ 64-bit alignment teq r2, r3 @ does this task own it... beq 1f @ this task doesn't own Concan regs -- use its save area msr cpsr_c, ip @ restore interrupt mode mov r0, r2 mov r2, #MMX_SIZE b memcpy 1: @ this task owns Concan regs -- load them directly mov r0, r1 mov r1, #0 @ don't clear CUP/MUP mov r3, lr @ preserve return address bl concan_load msr cpsr_c, ip @ restore interrupt mode ret r3 ENDPROC(iwmmxt_task_restore) /* * Concan handling on task switch * * r0 = next thread_info pointer * * Called only from the iwmmxt notifier with task preemption disabled. */ ENTRY(iwmmxt_task_switch) XSC(mrc p15, 0, r1, c15, c1, 0) PJ4(mrc p15, 0, r1, c1, c0, 2) @ CP0 and CP1 accessible? XSC(tst r1, #0x3) PJ4(tst r1, #0xf) bne 1f @ yes: block them for next task ldr r2, =concan_owner add r3, r0, #TI_IWMMXT_STATE @ get next task Concan save area ldr r2, [r2] @ get current Concan owner teq r2, r3 @ next task owns it? retne lr @ no: leave Concan disabled 1: @ flip Concan access XSC(eor r1, r1, #0x3) XSC(mcr p15, 0, r1, c15, c1, 0) PJ4(eor r1, r1, #0xf) PJ4(mcr p15, 0, r1, c1, c0, 2) mrc p15, 0, r1, c2, c0, 0 sub pc, lr, r1, lsr #32 @ cpwait and return ENDPROC(iwmmxt_task_switch) /* * Remove Concan ownership of given task * * r0 = struct thread_info pointer */ ENTRY(iwmmxt_task_release) mrs r2, cpsr orr ip, r2, #PSR_I_BIT @ disable interrupts msr cpsr_c, ip ldr r3, =concan_owner add r0, r0, #TI_IWMMXT_STATE @ get task Concan save area ldr r1, [r3] @ get current Concan owner eors r0, r0, r1 @ if equal... streq r0, [r3] @ then clear ownership msr cpsr_c, r2 @ restore interrupts ret lr ENDPROC(iwmmxt_task_release) .data .align 2 concan_owner: .word 0 |