aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/asm-offsets.c3
-rw-r--r--arch/arm/vfp/vfphw.S43
-rw-r--r--arch/arm/vfp/vfpmodule.c98
3 files changed, 89 insertions, 55 deletions
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
index 927522cfc12e..16baba2e4369 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -59,6 +59,9 @@ int main(void)
59 DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); 59 DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value));
60 DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); 60 DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
61 DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); 61 DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
62#ifdef CONFIG_SMP
63 DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu));
64#endif
62#ifdef CONFIG_ARM_THUMBEE 65#ifdef CONFIG_ARM_THUMBEE
63 DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); 66 DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state));
64#endif 67#endif
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index 404538ae591d..2d30c7f6edd3 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -82,19 +82,22 @@ ENTRY(vfp_support_entry)
82 ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state 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 @ this thread owns the hw context? 84 cmp r4, r10 @ this thread owns the hw context?
85#ifndef CONFIG_SMP
86 @ For UP, checking that this thread owns the hw context is
87 @ sufficient to determine that the hardware state is valid.
85 beq vfp_hw_state_valid 88 beq vfp_hw_state_valid
86 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.
93
87 VFPFMXR FPEXC, r5 @ enable VFP, disable any pending 94 VFPFMXR FPEXC, r5 @ enable VFP, disable any pending
88 @ exceptions, so we can get at the 95 @ exceptions, so we can get at the
89 @ rest of it 96 @ rest of it
90 97
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
95 DBGSTR1 "save old state %p", r4 98 DBGSTR1 "save old state %p", r4
96 cmp r4, #0 99 cmp r4, #0 @ if the vfp_current_hw_state is NULL
97 beq no_old_VFP_process 100 beq vfp_reload_hw @ then the hw state needs reloading
98 VFPFSTMIA r4, r5 @ save the working registers 101 VFPFSTMIA r4, r5 @ save the working registers
99 VFPFMRX r5, FPSCR @ current status 102 VFPFMRX r5, FPSCR @ current status
100#ifndef CONFIG_CPU_FEROCEON 103#ifndef CONFIG_CPU_FEROCEON
@@ -107,11 +110,33 @@ ENTRY(vfp_support_entry)
1071: 1101:
108#endif 111#endif
109 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 112 stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
110 @ and point r4 at the word at the 113vfp_reload_hw:
111 @ 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
112#endif 138#endif
113 139
114no_old_VFP_process:
115 DBGSTR1 "load state %p", r10 140 DBGSTR1 "load state %p", r10
116 str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer 141 str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer
117 @ Load the saved state back into the VFP 142 @ Load the saved state back into the VFP
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 3640351171b8..08ff93fa533c 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -35,18 +35,51 @@ void vfp_null_entry(void);
35void (*vfp_vector)(void) = vfp_null_entry; 35void (*vfp_vector)(void) = vfp_null_entry;
36 36
37/* 37/*
38 * Dual-use variable.
39 * Used in startup: set to non-zero if VFP checks fail
40 * After startup, holds VFP architecture
41 */
42unsigned int VFP_arch;
43
44/*
38 * The pointer to the vfpstate structure of the thread which currently 45 * The pointer to the vfpstate structure of the thread which currently
39 * owns the context held in the VFP hardware, or NULL if the hardware 46 * owns the context held in the VFP hardware, or NULL if the hardware
40 * context is invalid. 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.
41 */ 52 */
42union vfp_state *vfp_current_hw_state[NR_CPUS]; 53union vfp_state *vfp_current_hw_state[NR_CPUS];
43 54
44/* 55/*
45 * Dual-use variable. 56 * Is 'thread's most up to date state stored in this CPUs hardware?
46 * Used in startup: set to non-zero if VFP checks fail 57 * Must be called from non-preemptible context.
47 * After startup, holds VFP architecture
48 */ 58 */
49unsigned int VFP_arch; 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}
50 83
51/* 84/*
52 * Per-thread VFP initialization. 85 * Per-thread VFP initialization.
@@ -60,6 +93,9 @@ static void vfp_thread_flush(struct thread_info *thread)
60 93
61 vfp->hard.fpexc = FPEXC_EN; 94 vfp->hard.fpexc = FPEXC_EN;
62 vfp->hard.fpscr = FPSCR_ROUND_NEAREST; 95 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
96#ifdef CONFIG_SMP
97 vfp->hard.cpu = NR_CPUS;
98#endif
63 99
64 /* 100 /*
65 * Disable VFP to ensure we initialize it first. We must ensure 101 * Disable VFP to ensure we initialize it first. We must ensure
@@ -90,6 +126,9 @@ static void vfp_thread_copy(struct thread_info *thread)
90 126
91 vfp_sync_hwstate(parent); 127 vfp_sync_hwstate(parent);
92 thread->vfpstate = parent->vfpstate; 128 thread->vfpstate = parent->vfpstate;
129#ifdef CONFIG_SMP
130 thread->vfpstate.hard.cpu = NR_CPUS;
131#endif
93} 132}
94 133
95/* 134/*
@@ -135,17 +174,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
135 * case the thread migrates to a different CPU. The 174 * case the thread migrates to a different CPU. The
136 * restoring is done lazily. 175 * restoring is done lazily.
137 */ 176 */
138 if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) { 177 if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
139 vfp_save_state(vfp_current_hw_state[cpu], fpexc); 178 vfp_save_state(vfp_current_hw_state[cpu], fpexc);
140 vfp_current_hw_state[cpu]->hard.cpu = cpu;
141 }
142 /*
143 * Thread migration, just force the reloading of the
144 * state on the new CPU in case the VFP registers
145 * contain stale data.
146 */
147 if (thread->vfpstate.hard.cpu != cpu)
148 vfp_current_hw_state[cpu] = NULL;
149#endif 179#endif
150 180
151 /* 181 /*
@@ -449,15 +479,15 @@ static void vfp_pm_init(void)
449static inline void vfp_pm_init(void) { } 479static inline void vfp_pm_init(void) { }
450#endif /* CONFIG_PM */ 480#endif /* CONFIG_PM */
451 481
482/*
483 * Ensure that the VFP state stored in 'thread->vfpstate' is up to date
484 * with the hardware state.
485 */
452void vfp_sync_hwstate(struct thread_info *thread) 486void vfp_sync_hwstate(struct thread_info *thread)
453{ 487{
454 unsigned int cpu = get_cpu(); 488 unsigned int cpu = get_cpu();
455 489
456 /* 490 if (vfp_state_in_hw(cpu, thread)) {
457 * If the thread we're interested in is the current owner of the
458 * hardware VFP state, then we need to save its state.
459 */
460 if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
461 u32 fpexc = fmrx(FPEXC); 491 u32 fpexc = fmrx(FPEXC);
462 492
463 /* 493 /*
@@ -471,36 +501,13 @@ void vfp_sync_hwstate(struct thread_info *thread)
471 put_cpu(); 501 put_cpu();
472} 502}
473 503
504/* Ensure that the thread reloads the hardware VFP state on the next use. */
474void vfp_flush_hwstate(struct thread_info *thread) 505void vfp_flush_hwstate(struct thread_info *thread)
475{ 506{
476 unsigned int cpu = get_cpu(); 507 unsigned int cpu = get_cpu();
477 508
478 /* 509 vfp_force_reload(cpu, thread);
479 * If the thread we're interested in is the current owner of the
480 * hardware VFP state, then we need to save its state.
481 */
482 if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
483 u32 fpexc = fmrx(FPEXC);
484 510
485 fmxr(FPEXC, fpexc & ~FPEXC_EN);
486
487 /*
488 * Set the context to NULL to force a reload the next time
489 * the thread uses the VFP.
490 */
491 vfp_current_hw_state[cpu] = NULL;
492 }
493
494#ifdef CONFIG_SMP
495 /*
496 * For SMP we still have to take care of the case where the thread
497 * migrates to another CPU and then back to the original CPU on which
498 * the last VFP user is still the same thread. Mark the thread VFP
499 * state as belonging to a non-existent CPU so that the saved one will
500 * be reloaded in the above case.
501 */
502 thread->vfpstate.hard.cpu = NR_CPUS;
503#endif
504 put_cpu(); 511 put_cpu();
505} 512}
506 513
@@ -519,8 +526,7 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action,
519 void *hcpu) 526 void *hcpu)
520{ 527{
521 if (action == CPU_DYING || action == CPU_DYING_FROZEN) { 528 if (action == CPU_DYING || action == CPU_DYING_FROZEN) {
522 unsigned int cpu = (long)hcpu; 529 vfp_force_reload((long)hcpu, current_thread_info());
523 vfp_current_hw_state[cpu] = NULL;
524 } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 530 } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
525 vfp_enable(NULL); 531 vfp_enable(NULL);
526 return NOTIFY_OK; 532 return NOTIFY_OK;