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