| /* | |
| * arch/arm/kernel/crunch-bits.S | |
| * Cirrus MaverickCrunch context switching and handling | |
| * | |
| * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | |
| * | |
| * Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is | |
| * Copyright (c) 2003-2004, MontaVista Software, Inc. | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License version 2 as | |
| * published by the Free Software Foundation. | |
| */ | |
| #include <linux/linkage.h> | |
| #include <asm/ptrace.h> | |
| #include <asm/thread_info.h> | |
| #include <asm/asm-offsets.h> | |
| #include <mach/ep93xx-regs.h> | |
| /* | |
| * We can't use hex constants here due to a bug in gas. | |
| */ | |
| #define CRUNCH_MVDX0 0 | |
| #define CRUNCH_MVDX1 8 | |
| #define CRUNCH_MVDX2 16 | |
| #define CRUNCH_MVDX3 24 | |
| #define CRUNCH_MVDX4 32 | |
| #define CRUNCH_MVDX5 40 | |
| #define CRUNCH_MVDX6 48 | |
| #define CRUNCH_MVDX7 56 | |
| #define CRUNCH_MVDX8 64 | |
| #define CRUNCH_MVDX9 72 | |
| #define CRUNCH_MVDX10 80 | |
| #define CRUNCH_MVDX11 88 | |
| #define CRUNCH_MVDX12 96 | |
| #define CRUNCH_MVDX13 104 | |
| #define CRUNCH_MVDX14 112 | |
| #define CRUNCH_MVDX15 120 | |
| #define CRUNCH_MVAX0L 128 | |
| #define CRUNCH_MVAX0M 132 | |
| #define CRUNCH_MVAX0H 136 | |
| #define CRUNCH_MVAX1L 140 | |
| #define CRUNCH_MVAX1M 144 | |
| #define CRUNCH_MVAX1H 148 | |
| #define CRUNCH_MVAX2L 152 | |
| #define CRUNCH_MVAX2M 156 | |
| #define CRUNCH_MVAX2H 160 | |
| #define CRUNCH_MVAX3L 164 | |
| #define CRUNCH_MVAX3M 168 | |
| #define CRUNCH_MVAX3H 172 | |
| #define CRUNCH_DSPSC 176 | |
| #define CRUNCH_SIZE 184 | |
| .text | |
| /* | |
| * Lazy switching of crunch coprocessor context | |
| * | |
| * r10 = struct thread_info pointer | |
| * r9 = ret_from_exception | |
| * lr = undefined instr exit | |
| * | |
| * called from prefetch exception handler with interrupts disabled | |
| */ | |
| ENTRY(crunch_task_enable) | |
| ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr | |
| ldr r1, [r8, #0x80] | |
| tst r1, #0x00800000 @ access to crunch enabled? | |
| movne pc, lr @ if so no business here | |
| mov r3, #0xaa @ unlock syscon swlock | |
| str r3, [r8, #0xc0] | |
| orr r1, r1, #0x00800000 @ enable access to crunch | |
| str r1, [r8, #0x80] | |
| ldr r3, =crunch_owner | |
| add r0, r10, #TI_CRUNCH_STATE @ get task crunch save area | |
| ldr r2, [sp, #60] @ current task pc value | |
| ldr r1, [r3] @ get current crunch owner | |
| str r0, [r3] @ this task now owns crunch | |
| sub r2, r2, #4 @ adjust pc back | |
| str r2, [sp, #60] | |
| ldr r2, [r8, #0x80] | |
| mov r2, r2 @ flush out enable (@@@) | |
| teq r1, #0 @ test for last ownership | |
| mov lr, r9 @ normal exit from exception | |
| beq crunch_load @ no owner, skip save | |
| crunch_save: | |
| cfstr64 mvdx0, [r1, #CRUNCH_MVDX0] @ save 64b registers | |
| cfstr64 mvdx1, [r1, #CRUNCH_MVDX1] | |
| cfstr64 mvdx2, [r1, #CRUNCH_MVDX2] | |
| cfstr64 mvdx3, [r1, #CRUNCH_MVDX3] | |
| cfstr64 mvdx4, [r1, #CRUNCH_MVDX4] | |
| cfstr64 mvdx5, [r1, #CRUNCH_MVDX5] | |
| cfstr64 mvdx6, [r1, #CRUNCH_MVDX6] | |
| cfstr64 mvdx7, [r1, #CRUNCH_MVDX7] | |
| cfstr64 mvdx8, [r1, #CRUNCH_MVDX8] | |
| cfstr64 mvdx9, [r1, #CRUNCH_MVDX9] | |
| cfstr64 mvdx10, [r1, #CRUNCH_MVDX10] | |
| cfstr64 mvdx11, [r1, #CRUNCH_MVDX11] | |
| cfstr64 mvdx12, [r1, #CRUNCH_MVDX12] | |
| cfstr64 mvdx13, [r1, #CRUNCH_MVDX13] | |
| cfstr64 mvdx14, [r1, #CRUNCH_MVDX14] | |
| cfstr64 mvdx15, [r1, #CRUNCH_MVDX15] | |
| #ifdef __ARMEB__ | |
| #error fix me for ARMEB | |
| #endif | |
| cfmv32al mvfx0, mvax0 @ save 72b accumulators | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX0L] | |
| cfmv32am mvfx0, mvax0 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX0M] | |
| cfmv32ah mvfx0, mvax0 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX0H] | |
| cfmv32al mvfx0, mvax1 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX1L] | |
| cfmv32am mvfx0, mvax1 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX1M] | |
| cfmv32ah mvfx0, mvax1 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX1H] | |
| cfmv32al mvfx0, mvax2 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX2L] | |
| cfmv32am mvfx0, mvax2 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX2M] | |
| cfmv32ah mvfx0, mvax2 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX2H] | |
| cfmv32al mvfx0, mvax3 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX3L] | |
| cfmv32am mvfx0, mvax3 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX3M] | |
| cfmv32ah mvfx0, mvax3 | |
| cfstr32 mvfx0, [r1, #CRUNCH_MVAX3H] | |
| cfmv32sc mvdx0, dspsc @ save status word | |
| cfstr64 mvdx0, [r1, #CRUNCH_DSPSC] | |
| teq r0, #0 @ anything to load? | |
| cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered | |
| moveq pc, lr | |
| crunch_load: | |
| cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word | |
| cfmvsc32 dspsc, mvdx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX0L] @ load 72b accumulators | |
| cfmval32 mvax0, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX0M] | |
| cfmvam32 mvax0, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX0H] | |
| cfmvah32 mvax0, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX1L] | |
| cfmval32 mvax1, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX1M] | |
| cfmvam32 mvax1, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX1H] | |
| cfmvah32 mvax1, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX2L] | |
| cfmval32 mvax2, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX2M] | |
| cfmvam32 mvax2, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX2H] | |
| cfmvah32 mvax2, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX3L] | |
| cfmval32 mvax3, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX3M] | |
| cfmvam32 mvax3, mvfx0 | |
| cfldr32 mvfx0, [r0, #CRUNCH_MVAX3H] | |
| cfmvah32 mvax3, mvfx0 | |
| cfldr64 mvdx0, [r0, #CRUNCH_MVDX0] @ load 64b registers | |
| cfldr64 mvdx1, [r0, #CRUNCH_MVDX1] | |
| cfldr64 mvdx2, [r0, #CRUNCH_MVDX2] | |
| cfldr64 mvdx3, [r0, #CRUNCH_MVDX3] | |
| cfldr64 mvdx4, [r0, #CRUNCH_MVDX4] | |
| cfldr64 mvdx5, [r0, #CRUNCH_MVDX5] | |
| cfldr64 mvdx6, [r0, #CRUNCH_MVDX6] | |
| cfldr64 mvdx7, [r0, #CRUNCH_MVDX7] | |
| cfldr64 mvdx8, [r0, #CRUNCH_MVDX8] | |
| cfldr64 mvdx9, [r0, #CRUNCH_MVDX9] | |
| cfldr64 mvdx10, [r0, #CRUNCH_MVDX10] | |
| cfldr64 mvdx11, [r0, #CRUNCH_MVDX11] | |
| cfldr64 mvdx12, [r0, #CRUNCH_MVDX12] | |
| cfldr64 mvdx13, [r0, #CRUNCH_MVDX13] | |
| cfldr64 mvdx14, [r0, #CRUNCH_MVDX14] | |
| cfldr64 mvdx15, [r0, #CRUNCH_MVDX15] | |
| mov pc, lr | |
| /* | |
| * Back up crunch 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(crunch_task_disable) | |
| stmfd sp!, {r4, r5, lr} | |
| mrs ip, cpsr | |
| orr r2, ip, #PSR_I_BIT @ disable interrupts | |
| msr cpsr_c, r2 | |
| ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr | |
| ldr r3, =crunch_owner | |
| add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area | |
| ldr r1, [r3] @ get current crunch 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 | |
| ldr r5, [r4, #0x80] @ enable access to crunch | |
| mov r2, #0xaa | |
| str r2, [r4, #0xc0] | |
| orr r5, r5, #0x00800000 | |
| str r5, [r4, #0x80] | |
| mov r0, #0 @ nothing to load | |
| str r0, [r3] @ no more current owner | |
| ldr r2, [r4, #0x80] @ flush out enable (@@@) | |
| mov r2, r2 | |
| bl crunch_save | |
| mov r2, #0xaa @ disable access to crunch | |
| str r2, [r4, #0xc0] | |
| bic r5, r5, #0x00800000 | |
| str r5, [r4, #0x80] | |
| ldr r5, [r4, #0x80] @ flush out enable (@@@) | |
| mov r5, r5 | |
| 1: msr cpsr_c, ip @ restore interrupt mode | |
| ldmfd sp!, {r4, r5, pc} | |
| /* | |
| * Copy crunch state to given memory address | |
| * | |
| * r0 = struct thread_info pointer of target task | |
| * r1 = memory address where to store crunch state | |
| * | |
| * this is called mainly in the creation of signal stack frames | |
| */ | |
| ENTRY(crunch_task_copy) | |
| mrs ip, cpsr | |
| orr r2, ip, #PSR_I_BIT @ disable interrupts | |
| msr cpsr_c, r2 | |
| ldr r3, =crunch_owner | |
| add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area | |
| ldr r3, [r3] @ get current crunch owner | |
| teq r2, r3 @ does this task own it... | |
| beq 1f | |
| @ current crunch values are in the task save area | |
| msr cpsr_c, ip @ restore interrupt mode | |
| mov r0, r1 | |
| mov r1, r2 | |
| mov r2, #CRUNCH_SIZE | |
| b memcpy | |
| 1: @ this task owns crunch regs -- grab a copy from there | |
| mov r0, #0 @ nothing to load | |
| mov r3, lr @ preserve return address | |
| bl crunch_save | |
| msr cpsr_c, ip @ restore interrupt mode | |
| mov pc, r3 | |
| /* | |
| * Restore crunch state from given memory address | |
| * | |
| * r0 = struct thread_info pointer of target task | |
| * r1 = memory address where to get crunch state from | |
| * | |
| * this is used to restore crunch state when unwinding a signal stack frame | |
| */ | |
| ENTRY(crunch_task_restore) | |
| mrs ip, cpsr | |
| orr r2, ip, #PSR_I_BIT @ disable interrupts | |
| msr cpsr_c, r2 | |
| ldr r3, =crunch_owner | |
| add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area | |
| ldr r3, [r3] @ get current crunch owner | |
| teq r2, r3 @ does this task own it... | |
| beq 1f | |
| @ this task doesn't own crunch regs -- use its save area | |
| msr cpsr_c, ip @ restore interrupt mode | |
| mov r0, r2 | |
| mov r2, #CRUNCH_SIZE | |
| b memcpy | |
| 1: @ this task owns crunch regs -- load them directly | |
| mov r0, r1 | |
| mov r1, #0 @ nothing to save | |
| mov r3, lr @ preserve return address | |
| bl crunch_load | |
| msr cpsr_c, ip @ restore interrupt mode | |
| mov pc, r3 |