diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
| commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
| tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /arch/arm/vfp | |
| parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) | |
Patched in Tegra support.
Diffstat (limited to 'arch/arm/vfp')
| -rw-r--r-- | arch/arm/vfp/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/vfp/entry.S | 17 | ||||
| -rw-r--r-- | arch/arm/vfp/vfphw.S | 69 | ||||
| -rw-r--r-- | arch/arm/vfp/vfpmodule.c | 304 |
4 files changed, 139 insertions, 253 deletions
diff --git a/arch/arm/vfp/Makefile b/arch/arm/vfp/Makefile index a81404c09d5..6de73aab019 100644 --- a/arch/arm/vfp/Makefile +++ b/arch/arm/vfp/Makefile | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | # ccflags-y := -DDEBUG | 7 | # ccflags-y := -DDEBUG |
| 8 | # asflags-y := -DDEBUG | 8 | # asflags-y := -DDEBUG |
| 9 | 9 | ||
| 10 | KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp -mfloat-abi=soft) | 10 | KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp) |
| 11 | LDFLAGS +=--no-warn-mismatch | 11 | LDFLAGS +=--no-warn-mismatch |
| 12 | 12 | ||
| 13 | obj-y += vfp.o | 13 | obj-y += vfp.o |
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S index cc926c98598..c1a97840258 100644 --- a/arch/arm/vfp/entry.S +++ b/arch/arm/vfp/entry.S | |||
| @@ -7,20 +7,18 @@ | |||
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
| 9 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
| 10 | * | ||
| 11 | * Basic entry code, called from the kernel's undefined instruction trap. | ||
| 12 | * r0 = faulted instruction | ||
| 13 | * r2 = faulted PC+4 | ||
| 14 | * r9 = successful return | ||
| 15 | * r10 = thread_info structure | ||
| 16 | * lr = failure return | ||
| 10 | */ | 17 | */ |
| 11 | #include <asm/thread_info.h> | 18 | #include <asm/thread_info.h> |
| 12 | #include <asm/vfpmacros.h> | 19 | #include <asm/vfpmacros.h> |
| 13 | #include "../kernel/entry-header.S" | 20 | #include "../kernel/entry-header.S" |
| 14 | 21 | ||
| 15 | @ VFP entry point. | ||
| 16 | @ | ||
| 17 | @ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb) | ||
| 18 | @ r2 = PC value to resume execution after successful emulation | ||
| 19 | @ r9 = normal "successful" return address | ||
| 20 | @ r10 = this threads thread_info structure | ||
| 21 | @ lr = unrecognised instruction return address | ||
| 22 | @ IRQs disabled. | ||
| 23 | @ | ||
| 24 | ENTRY(do_vfp) | 22 | ENTRY(do_vfp) |
| 25 | #ifdef CONFIG_PREEMPT | 23 | #ifdef CONFIG_PREEMPT |
| 26 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | 24 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count |
| @@ -28,6 +26,7 @@ ENTRY(do_vfp) | |||
| 28 | str r11, [r10, #TI_PREEMPT] | 26 | str r11, [r10, #TI_PREEMPT] |
| 29 | #endif | 27 | #endif |
| 30 | enable_irq | 28 | enable_irq |
| 29 | str r2, [sp, #S_PC] @ update regs->ARM_pc for Thumb 2 case | ||
| 31 | ldr r4, .LCvfp | 30 | ldr r4, .LCvfp |
| 32 | ldr r11, [r10, #TI_CPU] @ CPU number | 31 | ldr r11, [r10, #TI_CPU] @ CPU number |
| 33 | add r10, r10, #TI_VFPSTATE @ r10 = workspace | 32 | add r10, r10, #TI_VFPSTATE @ r10 = workspace |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index ea0349f6358..404538ae591 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | */ | 16 | */ |
| 17 | #include <asm/thread_info.h> | 17 | #include <asm/thread_info.h> |
| 18 | #include <asm/vfpmacros.h> | 18 | #include <asm/vfpmacros.h> |
| 19 | #include <linux/kern_levels.h> | ||
| 20 | #include "../kernel/entry-header.S" | 19 | #include "../kernel/entry-header.S" |
| 21 | 20 | ||
| 22 | .macro DBGSTR, str | 21 | .macro DBGSTR, str |
| @@ -25,7 +24,7 @@ | |||
| 25 | add r0, pc, #4 | 24 | add r0, pc, #4 |
| 26 | bl printk | 25 | bl printk |
| 27 | b 1f | 26 | b 1f |
| 28 | .asciz KERN_DEBUG "VFP: \str\n" | 27 | .asciz "<7>VFP: \str\n" |
| 29 | .balign 4 | 28 | .balign 4 |
| 30 | 1: ldmfd sp!, {r0-r3, ip, lr} | 29 | 1: ldmfd sp!, {r0-r3, ip, lr} |
| 31 | #endif | 30 | #endif |
| @@ -38,7 +37,7 @@ | |||
| 38 | add r0, pc, #4 | 37 | add r0, pc, #4 |
| 39 | bl printk | 38 | bl printk |
| 40 | b 1f | 39 | b 1f |
| 41 | .asciz KERN_DEBUG "VFP: \str\n" | 40 | .asciz "<7>VFP: \str\n" |
| 42 | .balign 4 | 41 | .balign 4 |
| 43 | 1: ldmfd sp!, {r0-r3, ip, lr} | 42 | 1: ldmfd sp!, {r0-r3, ip, lr} |
| 44 | #endif | 43 | #endif |
| @@ -53,7 +52,7 @@ | |||
| 53 | add r0, pc, #4 | 52 | add r0, pc, #4 |
| 54 | bl printk | 53 | bl printk |
| 55 | b 1f | 54 | b 1f |
| 56 | .asciz KERN_DEBUG "VFP: \str\n" | 55 | .asciz "<7>VFP: \str\n" |
| 57 | .balign 4 | 56 | .balign 4 |
| 58 | 1: ldmfd sp!, {r0-r3, ip, lr} | 57 | 1: ldmfd sp!, {r0-r3, ip, lr} |
| 59 | #endif | 58 | #endif |
| @@ -62,13 +61,13 @@ | |||
| 62 | 61 | ||
| 63 | @ VFP hardware support entry point. | 62 | @ VFP hardware support entry point. |
| 64 | @ | 63 | @ |
| 65 | @ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb) | 64 | @ r0 = faulted instruction |
| 66 | @ r2 = PC value to resume execution after successful emulation | 65 | @ r2 = faulted PC+4 |
| 67 | @ r9 = normal "successful" return address | 66 | @ r9 = successful return |
| 68 | @ r10 = vfp_state union | 67 | @ r10 = vfp_state union |
| 69 | @ r11 = CPU number | 68 | @ r11 = CPU number |
| 70 | @ lr = unrecognised instruction return address | 69 | @ lr = failure return |
| 71 | @ IRQs enabled. | 70 | |
| 72 | ENTRY(vfp_support_entry) | 71 | ENTRY(vfp_support_entry) |
| 73 | DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10 | 72 | DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10 |
| 74 | 73 | ||
| @@ -83,22 +82,19 @@ ENTRY(vfp_support_entry) | |||
| 83 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer | 82 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer |
| 84 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled | 83 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled |
| 85 | cmp r4, r10 @ this thread owns the hw context? | 84 | cmp r4, r10 @ this thread owns the hw context? |
| 86 | #ifndef CONFIG_SMP | ||
| 87 | @ For UP, checking that this thread owns the hw context is | ||
| 88 | @ sufficient to determine that the hardware state is valid. | ||
| 89 | beq vfp_hw_state_valid | 85 | beq vfp_hw_state_valid |
| 90 | 86 | ||
| 91 | @ On UP, we lazily save the VFP context. As a different | ||
| 92 | @ thread wants ownership of the VFP hardware, save the old | ||
| 93 | @ state if there was a previous (valid) owner. | ||
| 94 | |||
| 95 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | 87 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending |
| 96 | @ exceptions, so we can get at the | 88 | @ exceptions, so we can get at the |
| 97 | @ rest of it | 89 | @ rest of it |
| 98 | 90 | ||
| 91 | #ifndef CONFIG_SMP | ||
| 92 | @ Save out the current registers to the old thread state | ||
| 93 | @ No need for SMP since this is not done lazily | ||
| 94 | |||
| 99 | DBGSTR1 "save old state %p", r4 | 95 | DBGSTR1 "save old state %p", r4 |
| 100 | cmp r4, #0 @ if the vfp_current_hw_state is NULL | 96 | cmp r4, #0 |
| 101 | beq vfp_reload_hw @ then the hw state needs reloading | 97 | beq no_old_VFP_process |
| 102 | VFPFSTMIA r4, r5 @ save the working registers | 98 | VFPFSTMIA r4, r5 @ save the working registers |
| 103 | VFPFMRX r5, FPSCR @ current status | 99 | VFPFMRX r5, FPSCR @ current status |
| 104 | #ifndef CONFIG_CPU_FEROCEON | 100 | #ifndef CONFIG_CPU_FEROCEON |
| @@ -111,33 +107,11 @@ ENTRY(vfp_support_entry) | |||
| 111 | 1: | 107 | 1: |
| 112 | #endif | 108 | #endif |
| 113 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 | 109 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 |
| 114 | vfp_reload_hw: | 110 | @ and point r4 at the word at the |
| 115 | 111 | @ start of the register dump | |
| 116 | #else | ||
| 117 | @ For SMP, if this thread does not own the hw context, then we | ||
| 118 | @ need to reload it. No need to save the old state as on SMP, | ||
| 119 | @ we always save the state when we switch away from a thread. | ||
| 120 | bne vfp_reload_hw | ||
| 121 | |||
| 122 | @ This thread has ownership of the current hardware context. | ||
| 123 | @ However, it may have been migrated to another CPU, in which | ||
| 124 | @ case the saved state is newer than the hardware context. | ||
| 125 | @ Check this by looking at the CPU number which the state was | ||
| 126 | @ last loaded onto. | ||
| 127 | ldr ip, [r10, #VFP_CPU] | ||
| 128 | teq ip, r11 | ||
| 129 | beq vfp_hw_state_valid | ||
| 130 | |||
| 131 | vfp_reload_hw: | ||
| 132 | @ We're loading this threads state into the VFP hardware. Update | ||
| 133 | @ the CPU number which contains the most up to date VFP context. | ||
| 134 | str r11, [r10, #VFP_CPU] | ||
| 135 | |||
| 136 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | ||
| 137 | @ exceptions, so we can get at the | ||
| 138 | @ rest of it | ||
| 139 | #endif | 112 | #endif |
| 140 | 113 | ||
| 114 | no_old_VFP_process: | ||
| 141 | DBGSTR1 "load state %p", r10 | 115 | DBGSTR1 "load state %p", r10 |
| 142 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer | 116 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer |
| 143 | @ Load the saved state back into the VFP | 117 | @ Load the saved state back into the VFP |
| @@ -162,12 +136,9 @@ vfp_hw_state_valid: | |||
| 162 | @ exception before retrying branch | 136 | @ exception before retrying branch |
| 163 | @ out before setting an FPEXC that | 137 | @ out before setting an FPEXC that |
| 164 | @ stops us reading stuff | 138 | @ stops us reading stuff |
| 165 | VFPFMXR FPEXC, r1 @ Restore FPEXC last | 139 | VFPFMXR FPEXC, r1 @ restore FPEXC last |
| 166 | sub r2, r2, #4 @ Retry current instruction - if Thumb | 140 | sub r2, r2, #4 |
| 167 | str r2, [sp, #S_PC] @ mode it's two 16-bit instructions, | 141 | str r2, [sp, #S_PC] @ retry the instruction |
| 168 | @ else it's one 32-bit instruction, so | ||
| 169 | @ always subtract 4 from the following | ||
| 170 | @ instruction address. | ||
| 171 | #ifdef CONFIG_PREEMPT | 142 | #ifdef CONFIG_PREEMPT |
| 172 | get_thread_info r10 | 143 | get_thread_info r10 |
| 173 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count | 144 | ldr r4, [r10, #TI_PREEMPT] @ get preempt count |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 3b44e0dd0a9..e381dc68505 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
| @@ -8,24 +8,20 @@ | |||
| 8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
| 9 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
| 10 | */ | 10 | */ |
| 11 | #include <linux/module.h> | ||
| 11 | #include <linux/types.h> | 12 | #include <linux/types.h> |
| 12 | #include <linux/cpu.h> | 13 | #include <linux/cpu.h> |
| 13 | #include <linux/cpu_pm.h> | ||
| 14 | #include <linux/hardirq.h> | ||
| 15 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 16 | #include <linux/notifier.h> | 15 | #include <linux/notifier.h> |
| 17 | #include <linux/signal.h> | 16 | #include <linux/signal.h> |
| 18 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
| 19 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
| 20 | #include <linux/init.h> | 19 | #include <linux/init.h> |
| 21 | #include <linux/uaccess.h> | ||
| 22 | #include <linux/user.h> | ||
| 23 | 20 | ||
| 24 | #include <asm/cp15.h> | ||
| 25 | #include <asm/cputype.h> | 21 | #include <asm/cputype.h> |
| 26 | #include <asm/system_info.h> | ||
| 27 | #include <asm/thread_notify.h> | 22 | #include <asm/thread_notify.h> |
| 28 | #include <asm/vfp.h> | 23 | #include <asm/vfp.h> |
| 24 | #include <asm/cpu_pm.h> | ||
| 29 | 25 | ||
| 30 | #include "vfpinstr.h" | 26 | #include "vfpinstr.h" |
| 31 | #include "vfp.h" | 27 | #include "vfp.h" |
| @@ -40,51 +36,18 @@ void vfp_null_entry(void); | |||
| 40 | void (*vfp_vector)(void) = vfp_null_entry; | 36 | void (*vfp_vector)(void) = vfp_null_entry; |
| 41 | 37 | ||
| 42 | /* | 38 | /* |
| 43 | * Dual-use variable. | ||
| 44 | * Used in startup: set to non-zero if VFP checks fail | ||
| 45 | * After startup, holds VFP architecture | ||
| 46 | */ | ||
| 47 | unsigned int VFP_arch; | ||
| 48 | |||
| 49 | /* | ||
| 50 | * The pointer to the vfpstate structure of the thread which currently | 39 | * The pointer to the vfpstate structure of the thread which currently |
| 51 | * owns the context held in the VFP hardware, or NULL if the hardware | 40 | * owns the context held in the VFP hardware, or NULL if the hardware |
| 52 | * context is invalid. | 41 | * context is invalid. |
| 53 | * | ||
| 54 | * For UP, this is sufficient to tell which thread owns the VFP context. | ||
| 55 | * However, for SMP, we also need to check the CPU number stored in the | ||
| 56 | * saved state too to catch migrations. | ||
| 57 | */ | 42 | */ |
| 58 | union vfp_state *vfp_current_hw_state[NR_CPUS]; | 43 | union vfp_state *vfp_current_hw_state[NR_CPUS]; |
| 59 | 44 | ||
| 60 | /* | 45 | /* |
| 61 | * Is 'thread's most up to date state stored in this CPUs hardware? | 46 | * Dual-use variable. |
| 62 | * Must be called from non-preemptible context. | 47 | * Used in startup: set to non-zero if VFP checks fail |
| 63 | */ | 48 | * After startup, holds VFP architecture |
| 64 | static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread) | ||
| 65 | { | ||
| 66 | #ifdef CONFIG_SMP | ||
| 67 | if (thread->vfpstate.hard.cpu != cpu) | ||
| 68 | return false; | ||
| 69 | #endif | ||
| 70 | return vfp_current_hw_state[cpu] == &thread->vfpstate; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* | ||
| 74 | * Force a reload of the VFP context from the thread structure. We do | ||
| 75 | * this by ensuring that access to the VFP hardware is disabled, and | ||
| 76 | * clear vfp_current_hw_state. Must be called from non-preemptible context. | ||
| 77 | */ | 49 | */ |
| 78 | static void vfp_force_reload(unsigned int cpu, struct thread_info *thread) | 50 | unsigned int VFP_arch; |
| 79 | { | ||
| 80 | if (vfp_state_in_hw(cpu, thread)) { | ||
| 81 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||
| 82 | vfp_current_hw_state[cpu] = NULL; | ||
| 83 | } | ||
| 84 | #ifdef CONFIG_SMP | ||
| 85 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 86 | #endif | ||
| 87 | } | ||
| 88 | 51 | ||
| 89 | /* | 52 | /* |
| 90 | * Per-thread VFP initialization. | 53 | * Per-thread VFP initialization. |
| @@ -94,27 +57,21 @@ static void vfp_thread_flush(struct thread_info *thread) | |||
| 94 | union vfp_state *vfp = &thread->vfpstate; | 57 | union vfp_state *vfp = &thread->vfpstate; |
| 95 | unsigned int cpu; | 58 | unsigned int cpu; |
| 96 | 59 | ||
| 60 | memset(vfp, 0, sizeof(union vfp_state)); | ||
| 61 | |||
| 62 | vfp->hard.fpexc = FPEXC_EN; | ||
| 63 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | ||
| 64 | |||
| 97 | /* | 65 | /* |
| 98 | * Disable VFP to ensure we initialize it first. We must ensure | 66 | * Disable VFP to ensure we initialize it first. We must ensure |
| 99 | * that the modification of vfp_current_hw_state[] and hardware | 67 | * that the modification of vfp_current_hw_state[] and hardware disable |
| 100 | * disable are done for the same CPU and without preemption. | 68 | * are done for the same CPU and without preemption. |
| 101 | * | ||
| 102 | * Do this first to ensure that preemption won't overwrite our | ||
| 103 | * state saving should access to the VFP be enabled at this point. | ||
| 104 | */ | 69 | */ |
| 105 | cpu = get_cpu(); | 70 | cpu = get_cpu(); |
| 106 | if (vfp_current_hw_state[cpu] == vfp) | 71 | if (vfp_current_hw_state[cpu] == vfp) |
| 107 | vfp_current_hw_state[cpu] = NULL; | 72 | vfp_current_hw_state[cpu] = NULL; |
| 108 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | 73 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); |
| 109 | put_cpu(); | 74 | put_cpu(); |
| 110 | |||
| 111 | memset(vfp, 0, sizeof(union vfp_state)); | ||
| 112 | |||
| 113 | vfp->hard.fpexc = FPEXC_EN; | ||
| 114 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | ||
| 115 | #ifdef CONFIG_SMP | ||
| 116 | vfp->hard.cpu = NR_CPUS; | ||
| 117 | #endif | ||
| 118 | } | 75 | } |
| 119 | 76 | ||
| 120 | static void vfp_thread_exit(struct thread_info *thread) | 77 | static void vfp_thread_exit(struct thread_info *thread) |
| @@ -134,9 +91,6 @@ static void vfp_thread_copy(struct thread_info *thread) | |||
| 134 | 91 | ||
| 135 | vfp_sync_hwstate(parent); | 92 | vfp_sync_hwstate(parent); |
| 136 | thread->vfpstate = parent->vfpstate; | 93 | thread->vfpstate = parent->vfpstate; |
| 137 | #ifdef CONFIG_SMP | ||
| 138 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 139 | #endif | ||
| 140 | } | 94 | } |
| 141 | 95 | ||
| 142 | /* | 96 | /* |
| @@ -182,8 +136,17 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
| 182 | * case the thread migrates to a different CPU. The | 136 | * case the thread migrates to a different CPU. The |
| 183 | * restoring is done lazily. | 137 | * restoring is done lazily. |
| 184 | */ | 138 | */ |
| 185 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) | 139 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) { |
| 186 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); | 140 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); |
| 141 | vfp_current_hw_state[cpu]->hard.cpu = cpu; | ||
| 142 | } | ||
| 143 | /* | ||
| 144 | * Thread migration, just force the reloading of the | ||
| 145 | * state on the new CPU in case the VFP registers | ||
| 146 | * contain stale data. | ||
| 147 | */ | ||
| 148 | if (thread->vfpstate.hard.cpu != cpu) | ||
| 149 | vfp_current_hw_state[cpu] = NULL; | ||
| 187 | #endif | 150 | #endif |
| 188 | 151 | ||
| 189 | /* | 152 | /* |
| @@ -213,6 +176,35 @@ static struct notifier_block vfp_notifier_block = { | |||
| 213 | .notifier_call = vfp_notifier, | 176 | .notifier_call = vfp_notifier, |
| 214 | }; | 177 | }; |
| 215 | 178 | ||
| 179 | static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, | ||
| 180 | void *v) | ||
| 181 | { | ||
| 182 | u32 fpexc = fmrx(FPEXC); | ||
| 183 | unsigned int cpu = smp_processor_id(); | ||
| 184 | |||
| 185 | switch (cmd) { | ||
| 186 | case CPU_PM_ENTER: | ||
| 187 | if (vfp_current_hw_state[cpu]) { | ||
| 188 | fmxr(FPEXC, fpexc | FPEXC_EN); | ||
| 189 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); | ||
| 190 | /* force a reload when coming back from idle */ | ||
| 191 | vfp_current_hw_state[cpu] = NULL; | ||
| 192 | fmxr(FPEXC, fpexc & ~FPEXC_EN); | ||
| 193 | } | ||
| 194 | break; | ||
| 195 | case CPU_PM_ENTER_FAILED: | ||
| 196 | case CPU_PM_EXIT: | ||
| 197 | /* make sure VFP is disabled when leaving idle */ | ||
| 198 | fmxr(FPEXC, fpexc & ~FPEXC_EN); | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | return NOTIFY_OK; | ||
| 202 | } | ||
| 203 | |||
| 204 | static struct notifier_block vfp_cpu_pm_notifier_block = { | ||
| 205 | .notifier_call = vfp_cpu_pm_notifier, | ||
| 206 | }; | ||
| 207 | |||
| 216 | /* | 208 | /* |
| 217 | * Raise a SIGFPE for the current process. | 209 | * Raise a SIGFPE for the current process. |
| 218 | * sicode describes the signal being raised. | 210 | * sicode describes the signal being raised. |
| @@ -241,11 +233,11 @@ static void vfp_panic(char *reason, u32 inst) | |||
| 241 | { | 233 | { |
| 242 | int i; | 234 | int i; |
| 243 | 235 | ||
| 244 | pr_err("VFP: Error: %s\n", reason); | 236 | printk(KERN_ERR "VFP: Error: %s\n", reason); |
| 245 | pr_err("VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", | 237 | printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", |
| 246 | fmrx(FPEXC), fmrx(FPSCR), inst); | 238 | fmrx(FPEXC), fmrx(FPSCR), inst); |
| 247 | for (i = 0; i < 32; i += 2) | 239 | for (i = 0; i < 32; i += 2) |
| 248 | pr_err("VFP: s%2u: 0x%08x s%2u: 0x%08x\n", | 240 | printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n", |
| 249 | i, vfp_get_float(i), i+1, vfp_get_float(i+1)); | 241 | i, vfp_get_float(i), i+1, vfp_get_float(i+1)); |
| 250 | } | 242 | } |
| 251 | 243 | ||
| @@ -433,10 +425,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | |||
| 433 | 425 | ||
| 434 | static void vfp_enable(void *unused) | 426 | static void vfp_enable(void *unused) |
| 435 | { | 427 | { |
| 436 | u32 access; | 428 | u32 access = get_copro_access(); |
| 437 | |||
| 438 | BUG_ON(preemptible()); | ||
| 439 | access = get_copro_access(); | ||
| 440 | 429 | ||
| 441 | /* | 430 | /* |
| 442 | * Enable full access to VFP (cp10 and cp11) | 431 | * Enable full access to VFP (cp10 and cp11) |
| @@ -444,29 +433,35 @@ static void vfp_enable(void *unused) | |||
| 444 | set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); | 433 | set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); |
| 445 | } | 434 | } |
| 446 | 435 | ||
| 447 | #ifdef CONFIG_CPU_PM | 436 | #ifdef CONFIG_PM |
| 437 | #include <linux/syscore_ops.h> | ||
| 438 | |||
| 448 | static int vfp_pm_suspend(void) | 439 | static int vfp_pm_suspend(void) |
| 449 | { | 440 | { |
| 450 | struct thread_info *ti = current_thread_info(); | 441 | struct thread_info *ti = current_thread_info(); |
| 451 | u32 fpexc = fmrx(FPEXC); | 442 | u32 fpexc = fmrx(FPEXC); |
| 452 | 443 | ||
| 444 | /* If lazy disable, re-enable the VFP ready for it to be saved */ | ||
| 445 | if (vfp_current_hw_state[ti->cpu] != &ti->vfpstate) { | ||
| 446 | fpexc |= FPEXC_EN; | ||
| 447 | fmxr(FPEXC, fpexc); | ||
| 448 | } | ||
| 449 | |||
| 453 | /* if vfp is on, then save state for resumption */ | 450 | /* if vfp is on, then save state for resumption */ |
| 454 | if (fpexc & FPEXC_EN) { | 451 | if (fpexc & FPEXC_EN) { |
| 455 | pr_debug("%s: saving vfp state\n", __func__); | 452 | printk(KERN_DEBUG "%s: saving vfp state\n", __func__); |
| 456 | vfp_save_state(&ti->vfpstate, fpexc); | 453 | vfp_save_state(&ti->vfpstate, fpexc); |
| 457 | 454 | ||
| 458 | /* disable, just in case */ | 455 | /* disable, just in case */ |
| 459 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | 456 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); |
| 460 | } else if (vfp_current_hw_state[ti->cpu]) { | 457 | } else if (vfp_current_hw_state[ti->cpu]) { |
| 461 | #ifndef CONFIG_SMP | ||
| 462 | fmxr(FPEXC, fpexc | FPEXC_EN); | 458 | fmxr(FPEXC, fpexc | FPEXC_EN); |
| 463 | vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); | 459 | vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); |
| 464 | fmxr(FPEXC, fpexc); | 460 | fmxr(FPEXC, fpexc); |
| 465 | #endif | ||
| 466 | } | 461 | } |
| 467 | 462 | ||
| 468 | /* clear any information we had about last context state */ | 463 | /* clear any information we had about last context state */ |
| 469 | vfp_current_hw_state[ti->cpu] = NULL; | 464 | memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state)); |
| 470 | 465 | ||
| 471 | return 0; | 466 | return 0; |
| 472 | } | 467 | } |
| @@ -480,43 +475,29 @@ static void vfp_pm_resume(void) | |||
| 480 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | 475 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); |
| 481 | } | 476 | } |
| 482 | 477 | ||
| 483 | static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, | 478 | static struct syscore_ops vfp_pm_syscore_ops = { |
| 484 | void *v) | 479 | .suspend = vfp_pm_suspend, |
| 485 | { | 480 | .resume = vfp_pm_resume, |
| 486 | switch (cmd) { | ||
| 487 | case CPU_PM_ENTER: | ||
| 488 | vfp_pm_suspend(); | ||
| 489 | break; | ||
| 490 | case CPU_PM_ENTER_FAILED: | ||
| 491 | case CPU_PM_EXIT: | ||
| 492 | vfp_pm_resume(); | ||
| 493 | break; | ||
| 494 | } | ||
| 495 | return NOTIFY_OK; | ||
| 496 | } | ||
| 497 | |||
| 498 | static struct notifier_block vfp_cpu_pm_notifier_block = { | ||
| 499 | .notifier_call = vfp_cpu_pm_notifier, | ||
| 500 | }; | 481 | }; |
| 501 | 482 | ||
| 502 | static void vfp_pm_init(void) | 483 | static void vfp_pm_init(void) |
| 503 | { | 484 | { |
| 504 | cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block); | 485 | register_syscore_ops(&vfp_pm_syscore_ops); |
| 505 | } | 486 | } |
| 506 | 487 | ||
| 507 | #else | 488 | #else |
| 508 | static inline void vfp_pm_init(void) { } | 489 | static inline void vfp_pm_init(void) { } |
| 509 | #endif /* CONFIG_CPU_PM */ | 490 | #endif /* CONFIG_PM */ |
| 510 | 491 | ||
| 511 | /* | ||
| 512 | * Ensure that the VFP state stored in 'thread->vfpstate' is up to date | ||
| 513 | * with the hardware state. | ||
| 514 | */ | ||
| 515 | void vfp_sync_hwstate(struct thread_info *thread) | 492 | void vfp_sync_hwstate(struct thread_info *thread) |
| 516 | { | 493 | { |
| 517 | unsigned int cpu = get_cpu(); | 494 | unsigned int cpu = get_cpu(); |
| 518 | 495 | ||
| 519 | if (vfp_state_in_hw(cpu, thread)) { | 496 | /* |
| 497 | * If the thread we're interested in is the current owner of the | ||
| 498 | * hardware VFP state, then we need to save its state. | ||
| 499 | */ | ||
| 500 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { | ||
| 520 | u32 fpexc = fmrx(FPEXC); | 501 | u32 fpexc = fmrx(FPEXC); |
| 521 | 502 | ||
| 522 | /* | 503 | /* |
| @@ -530,101 +511,37 @@ void vfp_sync_hwstate(struct thread_info *thread) | |||
| 530 | put_cpu(); | 511 | put_cpu(); |
| 531 | } | 512 | } |
| 532 | 513 | ||
| 533 | /* Ensure that the thread reloads the hardware VFP state on the next use. */ | ||
| 534 | void vfp_flush_hwstate(struct thread_info *thread) | 514 | void vfp_flush_hwstate(struct thread_info *thread) |
| 535 | { | 515 | { |
| 536 | unsigned int cpu = get_cpu(); | 516 | unsigned int cpu = get_cpu(); |
| 537 | 517 | ||
| 538 | vfp_force_reload(cpu, thread); | ||
| 539 | |||
| 540 | put_cpu(); | ||
| 541 | } | ||
| 542 | |||
| 543 | /* | ||
| 544 | * Save the current VFP state into the provided structures and prepare | ||
| 545 | * for entry into a new function (signal handler). | ||
| 546 | */ | ||
| 547 | int vfp_preserve_user_clear_hwstate(struct user_vfp __user *ufp, | ||
| 548 | struct user_vfp_exc __user *ufp_exc) | ||
| 549 | { | ||
| 550 | struct thread_info *thread = current_thread_info(); | ||
| 551 | struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; | ||
| 552 | int err = 0; | ||
| 553 | |||
| 554 | /* Ensure that the saved hwstate is up-to-date. */ | ||
| 555 | vfp_sync_hwstate(thread); | ||
| 556 | |||
| 557 | /* | ||
| 558 | * Copy the floating point registers. There can be unused | ||
| 559 | * registers see asm/hwcap.h for details. | ||
| 560 | */ | ||
| 561 | err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, | ||
| 562 | sizeof(hwstate->fpregs)); | ||
| 563 | /* | 518 | /* |
| 564 | * Copy the status and control register. | 519 | * If the thread we're interested in is the current owner of the |
| 520 | * hardware VFP state, then we need to save its state. | ||
| 565 | */ | 521 | */ |
| 566 | __put_user_error(hwstate->fpscr, &ufp->fpscr, err); | 522 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { |
| 567 | 523 | u32 fpexc = fmrx(FPEXC); | |
| 568 | /* | ||
| 569 | * Copy the exception registers. | ||
| 570 | */ | ||
| 571 | __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); | ||
| 572 | __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); | ||
| 573 | __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); | ||
| 574 | |||
| 575 | if (err) | ||
| 576 | return -EFAULT; | ||
| 577 | |||
| 578 | /* Ensure that VFP is disabled. */ | ||
| 579 | vfp_flush_hwstate(thread); | ||
| 580 | |||
| 581 | /* | ||
| 582 | * As per the PCS, clear the length and stride bits for function | ||
| 583 | * entry. | ||
| 584 | */ | ||
| 585 | hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK); | ||
| 586 | return 0; | ||
| 587 | } | ||
| 588 | |||
| 589 | /* Sanitise and restore the current VFP state from the provided structures. */ | ||
| 590 | int vfp_restore_user_hwstate(struct user_vfp __user *ufp, | ||
| 591 | struct user_vfp_exc __user *ufp_exc) | ||
| 592 | { | ||
| 593 | struct thread_info *thread = current_thread_info(); | ||
| 594 | struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; | ||
| 595 | unsigned long fpexc; | ||
| 596 | int err = 0; | ||
| 597 | 524 | ||
| 598 | /* Disable VFP to avoid corrupting the new thread state. */ | 525 | fmxr(FPEXC, fpexc & ~FPEXC_EN); |
| 599 | vfp_flush_hwstate(thread); | ||
| 600 | 526 | ||
| 601 | /* | 527 | /* |
| 602 | * Copy the floating point registers. There can be unused | 528 | * Set the context to NULL to force a reload the next time |
| 603 | * registers see asm/hwcap.h for details. | 529 | * the thread uses the VFP. |
| 604 | */ | 530 | */ |
| 605 | err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, | 531 | vfp_current_hw_state[cpu] = NULL; |
| 606 | sizeof(hwstate->fpregs)); | 532 | } |
| 607 | /* | ||
| 608 | * Copy the status and control register. | ||
| 609 | */ | ||
| 610 | __get_user_error(hwstate->fpscr, &ufp->fpscr, err); | ||
| 611 | 533 | ||
| 534 | #ifdef CONFIG_SMP | ||
| 612 | /* | 535 | /* |
| 613 | * Sanitise and restore the exception registers. | 536 | * For SMP we still have to take care of the case where the thread |
| 537 | * migrates to another CPU and then back to the original CPU on which | ||
| 538 | * the last VFP user is still the same thread. Mark the thread VFP | ||
| 539 | * state as belonging to a non-existent CPU so that the saved one will | ||
| 540 | * be reloaded in the above case. | ||
| 614 | */ | 541 | */ |
| 615 | __get_user_error(fpexc, &ufp_exc->fpexc, err); | 542 | thread->vfpstate.hard.cpu = NR_CPUS; |
| 616 | 543 | #endif | |
| 617 | /* Ensure the VFP is enabled. */ | 544 | put_cpu(); |
| 618 | fpexc |= FPEXC_EN; | ||
| 619 | |||
| 620 | /* Ensure FPINST2 is invalid and the exception flag is cleared. */ | ||
| 621 | fpexc &= ~(FPEXC_EX | FPEXC_FP2V); | ||
| 622 | hwstate->fpexc = fpexc; | ||
| 623 | |||
| 624 | __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); | ||
| 625 | __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); | ||
| 626 | |||
| 627 | return err ? -EFAULT : 0; | ||
| 628 | } | 545 | } |
| 629 | 546 | ||
| 630 | /* | 547 | /* |
| @@ -642,7 +559,8 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action, | |||
| 642 | void *hcpu) | 559 | void *hcpu) |
| 643 | { | 560 | { |
| 644 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { | 561 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { |
| 645 | vfp_force_reload((long)hcpu, current_thread_info()); | 562 | unsigned int cpu = (long)hcpu; |
| 563 | vfp_current_hw_state[cpu] = NULL; | ||
| 646 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) | 564 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) |
| 647 | vfp_enable(NULL); | 565 | vfp_enable(NULL); |
| 648 | return NOTIFY_OK; | 566 | return NOTIFY_OK; |
| @@ -657,7 +575,7 @@ static int __init vfp_init(void) | |||
| 657 | unsigned int cpu_arch = cpu_architecture(); | 575 | unsigned int cpu_arch = cpu_architecture(); |
| 658 | 576 | ||
| 659 | if (cpu_arch >= CPU_ARCH_ARMv6) | 577 | if (cpu_arch >= CPU_ARCH_ARMv6) |
| 660 | on_each_cpu(vfp_enable, NULL, 1); | 578 | vfp_enable(NULL); |
| 661 | 579 | ||
| 662 | /* | 580 | /* |
| 663 | * First check that there is a VFP that we can use. | 581 | * First check that there is a VFP that we can use. |
| @@ -670,16 +588,18 @@ static int __init vfp_init(void) | |||
| 670 | barrier(); | 588 | barrier(); |
| 671 | vfp_vector = vfp_null_entry; | 589 | vfp_vector = vfp_null_entry; |
| 672 | 590 | ||
| 673 | pr_info("VFP support v0.3: "); | 591 | printk(KERN_INFO "VFP support v0.3: "); |
| 674 | if (VFP_arch) | 592 | if (VFP_arch) |
| 675 | pr_cont("not present\n"); | 593 | printk("not present\n"); |
| 676 | else if (vfpsid & FPSID_NODOUBLE) { | 594 | else if (vfpsid & FPSID_NODOUBLE) { |
| 677 | pr_cont("no double precision support\n"); | 595 | printk("no double precision support\n"); |
| 678 | } else { | 596 | } else { |
| 679 | hotcpu_notifier(vfp_hotplug, 0); | 597 | hotcpu_notifier(vfp_hotplug, 0); |
| 680 | 598 | ||
| 599 | smp_call_function(vfp_enable, NULL, 1); | ||
| 600 | |||
| 681 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ | 601 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ |
| 682 | pr_cont("implementor %02x architecture %d part %02x variant %x rev %x\n", | 602 | printk("implementor %02x architecture %d part %02x variant %x rev %x\n", |
| 683 | (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, | 603 | (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, |
| 684 | (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, | 604 | (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, |
| 685 | (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, | 605 | (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, |
| @@ -689,6 +609,7 @@ static int __init vfp_init(void) | |||
| 689 | vfp_vector = vfp_support_entry; | 609 | vfp_vector = vfp_support_entry; |
| 690 | 610 | ||
| 691 | thread_register_notifier(&vfp_notifier_block); | 611 | thread_register_notifier(&vfp_notifier_block); |
| 612 | cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block); | ||
| 692 | vfp_pm_init(); | 613 | vfp_pm_init(); |
| 693 | 614 | ||
| 694 | /* | 615 | /* |
| @@ -701,14 +622,11 @@ static int __init vfp_init(void) | |||
| 701 | elf_hwcap |= HWCAP_VFPv3; | 622 | elf_hwcap |= HWCAP_VFPv3; |
| 702 | 623 | ||
| 703 | /* | 624 | /* |
| 704 | * Check for VFPv3 D16 and VFPv4 D16. CPUs in | 625 | * Check for VFPv3 D16. CPUs in this configuration |
| 705 | * this configuration only have 16 x 64bit | 626 | * only have 16 x 64bit registers. |
| 706 | * registers. | ||
| 707 | */ | 627 | */ |
| 708 | if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK)) == 1) | 628 | if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK)) == 1) |
| 709 | elf_hwcap |= HWCAP_VFPv3D16; /* also v4-D16 */ | 629 | elf_hwcap |= HWCAP_VFPv3D16; |
| 710 | else | ||
| 711 | elf_hwcap |= HWCAP_VFPD32; | ||
| 712 | } | 630 | } |
| 713 | #endif | 631 | #endif |
| 714 | /* | 632 | /* |
| @@ -722,10 +640,8 @@ static int __init vfp_init(void) | |||
| 722 | if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) | 640 | if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) |
| 723 | elf_hwcap |= HWCAP_NEON; | 641 | elf_hwcap |= HWCAP_NEON; |
| 724 | #endif | 642 | #endif |
| 725 | #ifdef CONFIG_VFPv3 | ||
| 726 | if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000) | 643 | if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000) |
| 727 | elf_hwcap |= HWCAP_VFPv4; | 644 | elf_hwcap |= HWCAP_VFPv4; |
| 728 | #endif | ||
| 729 | } | 645 | } |
| 730 | } | 646 | } |
| 731 | return 0; | 647 | return 0; |
