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