aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/vfp
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r--arch/arm/vfp/vfphw.S63
-rw-r--r--arch/arm/vfp/vfpmodule.c127
2 files changed, 114 insertions, 76 deletions
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index 9897dcfc16d6..2d30c7f6edd3 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -77,27 +77,27 @@ ENTRY(vfp_support_entry)
77 bne look_for_VFP_exceptions @ VFP is already enabled 77 bne look_for_VFP_exceptions @ VFP is already enabled
78 78
79 DBGSTR1 "enable %x", r10 79 DBGSTR1 "enable %x", r10
80 ldr r3, last_VFP_context_address 80 ldr r3, vfp_current_hw_state_address
81 orr r1, r1, #FPEXC_EN @ user FPEXC has the enable bit set 81 orr r1, r1, #FPEXC_EN @ user FPEXC has the enable bit set
82 ldr r4, [r3, r11, lsl #2] @ last_VFP_context pointer 82 ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer
83 bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled 83 bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled
84 cmp r4, r10 84 cmp r4, r10 @ this thread owns the hw context?
85 beq check_for_exception @ we are returning to the same 85#ifndef CONFIG_SMP
86 @ process, so the registers are 86 @ For UP, checking that this thread owns the hw context is
87 @ still there. In this case, we do 87 @ sufficient to determine that the hardware state is valid.
88 @ not want to drop a pending exception. 88 beq vfp_hw_state_valid
89
90 @ On UP, we lazily save the VFP context. As a different
91 @ thread wants ownership of the VFP hardware, save the old
92 @ state if there was a previous (valid) owner.
89 93
90 VFPFMXR FPEXC, r5 @ enable VFP, disable any pending 94 VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
91 @ exceptions, so we can get at the 95 @ exceptions, so we can get at the
92 @ rest of it 96 @ rest of it
93 97
94#ifndef CONFIG_SMP
95 @ Save out the current registers to the old thread state
96 @ No need for SMP since this is not done lazily
97
98 DBGSTR1 "save old state %p", r4 98 DBGSTR1 "save old state %p", r4
99 cmp r4, #0 99 cmp r4, #0 @ if the vfp_current_hw_state is NULL
100 beq no_old_VFP_process 100 beq vfp_reload_hw @ then the hw state needs reloading
101 VFPFSTMIA r4, r5 @ save the working registers 101 VFPFSTMIA r4, r5 @ save the working registers
102 VFPFMRX r5, FPSCR @ current status 102 VFPFMRX r5, FPSCR @ current status
103#ifndef CONFIG_CPU_FEROCEON 103#ifndef CONFIG_CPU_FEROCEON
@@ -110,13 +110,35 @@ ENTRY(vfp_support_entry)
1101: 1101:
111#endif 111#endif
112 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 112 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
113 @ and point r4 at the word at the 113vfp_reload_hw:
114 @ start of the register dump 114
115#else
116 @ For SMP, if this thread does not own the hw context, then we
117 @ need to reload it. No need to save the old state as on SMP,
118 @ we always save the state when we switch away from a thread.
119 bne vfp_reload_hw
120
121 @ This thread has ownership of the current hardware context.
122 @ However, it may have been migrated to another CPU, in which
123 @ case the saved state is newer than the hardware context.
124 @ Check this by looking at the CPU number which the state was
125 @ last loaded onto.
126 ldr ip, [r10, #VFP_CPU]
127 teq ip, r11
128 beq vfp_hw_state_valid
129
130vfp_reload_hw:
131 @ We're loading this threads state into the VFP hardware. Update
132 @ the CPU number which contains the most up to date VFP context.
133 str r11, [r10, #VFP_CPU]
134
135 VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
136 @ exceptions, so we can get at the
137 @ rest of it
115#endif 138#endif
116 139
117no_old_VFP_process:
118 DBGSTR1 "load state %p", r10 140 DBGSTR1 "load state %p", r10
119 str r10, [r3, r11, lsl #2] @ update the last_VFP_context pointer 141 str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer
120 @ Load the saved state back into the VFP 142 @ Load the saved state back into the VFP
121 VFPFLDMIA r10, r5 @ reload the working registers while 143 VFPFLDMIA r10, r5 @ reload the working registers while
122 @ FPEXC is in a safe state 144 @ FPEXC is in a safe state
@@ -132,7 +154,8 @@ no_old_VFP_process:
132#endif 154#endif
133 VFPFMXR FPSCR, r5 @ restore status 155 VFPFMXR FPSCR, r5 @ restore status
134 156
135check_for_exception: 157@ The context stored in the VFP hardware is up to date with this thread
158vfp_hw_state_valid:
136 tst r1, #FPEXC_EX 159 tst r1, #FPEXC_EX
137 bne process_exception @ might as well handle the pending 160 bne process_exception @ might as well handle the pending
138 @ exception before retrying branch 161 @ exception before retrying branch
@@ -207,8 +230,8 @@ ENTRY(vfp_save_state)
207ENDPROC(vfp_save_state) 230ENDPROC(vfp_save_state)
208 231
209 .align 232 .align
210last_VFP_context_address: 233vfp_current_hw_state_address:
211 .word last_VFP_context 234 .word vfp_current_hw_state
212 235
213 .macro tbl_branch, base, tmp, shift 236 .macro tbl_branch, base, tmp, shift
214#ifdef CONFIG_THUMB2_KERNEL 237#ifdef CONFIG_THUMB2_KERNEL
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index f25e7ec89416..0a96f71f0abd 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -33,7 +33,6 @@ void vfp_support_entry(void);
33void vfp_null_entry(void); 33void vfp_null_entry(void);
34 34
35void (*vfp_vector)(void) = vfp_null_entry; 35void (*vfp_vector)(void) = vfp_null_entry;
36union vfp_state *last_VFP_context[NR_CPUS];
37 36
38/* 37/*
39 * Dual-use variable. 38 * Dual-use variable.
@@ -43,6 +42,46 @@ union vfp_state *last_VFP_context[NR_CPUS];
43unsigned int VFP_arch; 42unsigned int VFP_arch;
44 43
45/* 44/*
45 * The pointer to the vfpstate structure of the thread which currently
46 * owns the context held in the VFP hardware, or NULL if the hardware
47 * context is invalid.
48 *
49 * For UP, this is sufficient to tell which thread owns the VFP context.
50 * However, for SMP, we also need to check the CPU number stored in the
51 * saved state too to catch migrations.
52 */
53union vfp_state *vfp_current_hw_state[NR_CPUS];
54
55/*
56 * Is 'thread's most up to date state stored in this CPUs hardware?
57 * Must be called from non-preemptible context.
58 */
59static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread)
60{
61#ifdef CONFIG_SMP
62 if (thread->vfpstate.hard.cpu != cpu)
63 return false;
64#endif
65 return vfp_current_hw_state[cpu] == &thread->vfpstate;
66}
67
68/*
69 * Force a reload of the VFP context from the thread structure. We do
70 * this by ensuring that access to the VFP hardware is disabled, and
71 * clear last_VFP_context. Must be called from non-preemptible context.
72 */
73static void vfp_force_reload(unsigned int cpu, struct thread_info *thread)
74{
75 if (vfp_state_in_hw(cpu, thread)) {
76 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
77 vfp_current_hw_state[cpu] = NULL;
78 }
79#ifdef CONFIG_SMP
80 thread->vfpstate.hard.cpu = NR_CPUS;
81#endif
82}
83
84/*
46 * Per-thread VFP initialization. 85 * Per-thread VFP initialization.
47 */ 86 */
48static void vfp_thread_flush(struct thread_info *thread) 87static void vfp_thread_flush(struct thread_info *thread)
@@ -50,21 +89,27 @@ static void vfp_thread_flush(struct thread_info *thread)
50 union vfp_state *vfp = &thread->vfpstate; 89 union vfp_state *vfp = &thread->vfpstate;
51 unsigned int cpu; 90 unsigned int cpu;
52 91
53 memset(vfp, 0, sizeof(union vfp_state));
54
55 vfp->hard.fpexc = FPEXC_EN;
56 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
57
58 /* 92 /*
59 * Disable VFP to ensure we initialize it first. We must ensure 93 * Disable VFP to ensure we initialize it first. We must ensure
60 * that the modification of last_VFP_context[] and hardware disable 94 * that the modification of vfp_current_hw_state[] and hardware
61 * are done for the same CPU and without preemption. 95 * disable are done for the same CPU and without preemption.
96 *
97 * Do this first to ensure that preemption won't overwrite our
98 * state saving should access to the VFP be enabled at this point.
62 */ 99 */
63 cpu = get_cpu(); 100 cpu = get_cpu();
64 if (last_VFP_context[cpu] == vfp) 101 if (vfp_current_hw_state[cpu] == vfp)
65 last_VFP_context[cpu] = NULL; 102 vfp_current_hw_state[cpu] = NULL;
66 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); 103 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
67 put_cpu(); 104 put_cpu();
105
106 memset(vfp, 0, sizeof(union vfp_state));
107
108 vfp->hard.fpexc = FPEXC_EN;
109 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
110#ifdef CONFIG_SMP
111 vfp->hard.cpu = NR_CPUS;
112#endif
68} 113}
69 114
70static void vfp_thread_exit(struct thread_info *thread) 115static void vfp_thread_exit(struct thread_info *thread)
@@ -73,8 +118,8 @@ static void vfp_thread_exit(struct thread_info *thread)
73 union vfp_state *vfp = &thread->vfpstate; 118 union vfp_state *vfp = &thread->vfpstate;
74 unsigned int cpu = get_cpu(); 119 unsigned int cpu = get_cpu();
75 120
76 if (last_VFP_context[cpu] == vfp) 121 if (vfp_current_hw_state[cpu] == vfp)
77 last_VFP_context[cpu] = NULL; 122 vfp_current_hw_state[cpu] = NULL;
78 put_cpu(); 123 put_cpu();
79} 124}
80 125
@@ -84,6 +129,9 @@ static void vfp_thread_copy(struct thread_info *thread)
84 129
85 vfp_sync_hwstate(parent); 130 vfp_sync_hwstate(parent);
86 thread->vfpstate = parent->vfpstate; 131 thread->vfpstate = parent->vfpstate;
132#ifdef CONFIG_SMP
133 thread->vfpstate.hard.cpu = NR_CPUS;
134#endif
87} 135}
88 136
89/* 137/*
@@ -129,17 +177,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
129 * case the thread migrates to a different CPU. The 177 * case the thread migrates to a different CPU. The
130 * restoring is done lazily. 178 * restoring is done lazily.
131 */ 179 */
132 if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) { 180 if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
133 vfp_save_state(last_VFP_context[cpu], fpexc); 181 vfp_save_state(vfp_current_hw_state[cpu], fpexc);
134 last_VFP_context[cpu]->hard.cpu = cpu;
135 }
136 /*
137 * Thread migration, just force the reloading of the
138 * state on the new CPU in case the VFP registers
139 * contain stale data.
140 */
141 if (thread->vfpstate.hard.cpu != cpu)
142 last_VFP_context[cpu] = NULL;
143#endif 182#endif
144 183
145 /* 184 /*
@@ -415,7 +454,7 @@ static int vfp_pm_suspend(void)
415 } 454 }
416 455
417 /* clear any information we had about last context state */ 456 /* clear any information we had about last context state */
418 memset(last_VFP_context, 0, sizeof(last_VFP_context)); 457 memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state));
419 458
420 return 0; 459 return 0;
421} 460}
@@ -443,15 +482,15 @@ static void vfp_pm_init(void)
443static inline void vfp_pm_init(void) { } 482static inline void vfp_pm_init(void) { }
444#endif /* CONFIG_PM */ 483#endif /* CONFIG_PM */
445 484
485/*
486 * Ensure that the VFP state stored in 'thread->vfpstate' is up to date
487 * with the hardware state.
488 */
446void vfp_sync_hwstate(struct thread_info *thread) 489void vfp_sync_hwstate(struct thread_info *thread)
447{ 490{
448 unsigned int cpu = get_cpu(); 491 unsigned int cpu = get_cpu();
449 492
450 /* 493 if (vfp_state_in_hw(cpu, thread)) {
451 * If the thread we're interested in is the current owner of the
452 * hardware VFP state, then we need to save its state.
453 */
454 if (last_VFP_context[cpu] == &thread->vfpstate) {
455 u32 fpexc = fmrx(FPEXC); 494 u32 fpexc = fmrx(FPEXC);
456 495
457 /* 496 /*
@@ -465,36 +504,13 @@ void vfp_sync_hwstate(struct thread_info *thread)
465 put_cpu(); 504 put_cpu();
466} 505}
467 506
507/* Ensure that the thread reloads the hardware VFP state on the next use. */
468void vfp_flush_hwstate(struct thread_info *thread) 508void vfp_flush_hwstate(struct thread_info *thread)
469{ 509{
470 unsigned int cpu = get_cpu(); 510 unsigned int cpu = get_cpu();
471 511
472 /* 512 vfp_force_reload(cpu, thread);
473 * If the thread we're interested in is the current owner of the
474 * hardware VFP state, then we need to save its state.
475 */
476 if (last_VFP_context[cpu] == &thread->vfpstate) {
477 u32 fpexc = fmrx(FPEXC);
478
479 fmxr(FPEXC, fpexc & ~FPEXC_EN);
480
481 /*
482 * Set the context to NULL to force a reload the next time
483 * the thread uses the VFP.
484 */
485 last_VFP_context[cpu] = NULL;
486 }
487 513
488#ifdef CONFIG_SMP
489 /*
490 * For SMP we still have to take care of the case where the thread
491 * migrates to another CPU and then back to the original CPU on which
492 * the last VFP user is still the same thread. Mark the thread VFP
493 * state as belonging to a non-existent CPU so that the saved one will
494 * be reloaded in the above case.
495 */
496 thread->vfpstate.hard.cpu = NR_CPUS;
497#endif
498 put_cpu(); 514 put_cpu();
499} 515}
500 516
@@ -513,8 +529,7 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action,
513 void *hcpu) 529 void *hcpu)
514{ 530{
515 if (action == CPU_DYING || action == CPU_DYING_FROZEN) { 531 if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
516 unsigned int cpu = (long)hcpu; 532 vfp_force_reload((long)hcpu, current_thread_info());
517 last_VFP_context[cpu] = NULL;
518 } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 533 } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
519 vfp_enable(NULL); 534 vfp_enable(NULL);
520 return NOTIFY_OK; 535 return NOTIFY_OK;