aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/vfp
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
commit8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch)
treea8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /arch/arm/vfp
parent406089d01562f1e2bf9f089fd7637009ebaad589 (diff)
Patched in Tegra support.
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r--arch/arm/vfp/Makefile2
-rw-r--r--arch/arm/vfp/entry.S17
-rw-r--r--arch/arm/vfp/vfphw.S69
-rw-r--r--arch/arm/vfp/vfpmodule.c304
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
10KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp -mfloat-abi=soft) 10KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp)
11LDFLAGS +=--no-warn-mismatch 11LDFLAGS +=--no-warn-mismatch
12 12
13obj-y += vfp.o 13obj-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@
24ENTRY(do_vfp) 22ENTRY(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
301: ldmfd sp!, {r0-r3, ip, lr} 291: 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
431: ldmfd sp!, {r0-r3, ip, lr} 421: 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
581: ldmfd sp!, {r0-r3, ip, lr} 571: 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
72ENTRY(vfp_support_entry) 71ENTRY(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)
1111: 1071:
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
114vfp_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
131vfp_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
114no_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);
40void (*vfp_vector)(void) = vfp_null_entry; 36void (*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 */
47unsigned 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 */
58union vfp_state *vfp_current_hw_state[NR_CPUS]; 43union 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
64static 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 */
78static void vfp_force_reload(unsigned int cpu, struct thread_info *thread) 50unsigned 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
120static void vfp_thread_exit(struct thread_info *thread) 77static 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
179static 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
204static 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
434static void vfp_enable(void *unused) 426static 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
448static int vfp_pm_suspend(void) 439static 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
483static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd, 478static 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
498static struct notifier_block vfp_cpu_pm_notifier_block = {
499 .notifier_call = vfp_cpu_pm_notifier,
500}; 481};
501 482
502static void vfp_pm_init(void) 483static 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
508static inline void vfp_pm_init(void) { } 489static 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 */
515void vfp_sync_hwstate(struct thread_info *thread) 492void 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. */
534void vfp_flush_hwstate(struct thread_info *thread) 514void 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 */
547int 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. */
590int 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;