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