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