aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/vfp
diff options
context:
space:
mode:
authorAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
commitada47b5fe13d89735805b566185f4885f5a3f750 (patch)
tree644b88f8a71896307d71438e9b3af49126ffb22b /arch/arm/vfp
parent43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff)
parent3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff)
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r--arch/arm/vfp/vfpmodule.c163
1 files changed, 108 insertions, 55 deletions
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 2d7423af1197..315a540c7ce5 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -38,16 +38,75 @@ union vfp_state *last_VFP_context[NR_CPUS];
38 */ 38 */
39unsigned int VFP_arch; 39unsigned int VFP_arch;
40 40
41/*
42 * Per-thread VFP initialization.
43 */
44static void vfp_thread_flush(struct thread_info *thread)
45{
46 union vfp_state *vfp = &thread->vfpstate;
47 unsigned int cpu;
48
49 memset(vfp, 0, sizeof(union vfp_state));
50
51 vfp->hard.fpexc = FPEXC_EN;
52 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
53
54 /*
55 * Disable VFP to ensure we initialize it first. We must ensure
56 * that the modification of last_VFP_context[] and hardware disable
57 * are done for the same CPU and without preemption.
58 */
59 cpu = get_cpu();
60 if (last_VFP_context[cpu] == vfp)
61 last_VFP_context[cpu] = NULL;
62 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
63 put_cpu();
64}
65
66static void vfp_thread_exit(struct thread_info *thread)
67{
68 /* release case: Per-thread VFP cleanup. */
69 union vfp_state *vfp = &thread->vfpstate;
70 unsigned int cpu = get_cpu();
71
72 if (last_VFP_context[cpu] == vfp)
73 last_VFP_context[cpu] = NULL;
74 put_cpu();
75}
76
77/*
78 * When this function is called with the following 'cmd's, the following
79 * is true while this function is being run:
80 * THREAD_NOFTIFY_SWTICH:
81 * - the previously running thread will not be scheduled onto another CPU.
82 * - the next thread to be run (v) will not be running on another CPU.
83 * - thread->cpu is the local CPU number
84 * - not preemptible as we're called in the middle of a thread switch
85 * THREAD_NOTIFY_FLUSH:
86 * - the thread (v) will be running on the local CPU, so
87 * v === current_thread_info()
88 * - thread->cpu is the local CPU number at the time it is accessed,
89 * but may change at any time.
90 * - we could be preempted if tree preempt rcu is enabled, so
91 * it is unsafe to use thread->cpu.
92 * THREAD_NOTIFY_EXIT
93 * - the thread (v) will be running on the local CPU, so
94 * v === current_thread_info()
95 * - thread->cpu is the local CPU number at the time it is accessed,
96 * but may change at any time.
97 * - we could be preempted if tree preempt rcu is enabled, so
98 * it is unsafe to use thread->cpu.
99 */
41static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) 100static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
42{ 101{
43 struct thread_info *thread = v; 102 struct thread_info *thread = v;
44 union vfp_state *vfp;
45 __u32 cpu = thread->cpu;
46 103
47 if (likely(cmd == THREAD_NOTIFY_SWITCH)) { 104 if (likely(cmd == THREAD_NOTIFY_SWITCH)) {
48 u32 fpexc = fmrx(FPEXC); 105 u32 fpexc = fmrx(FPEXC);
49 106
50#ifdef CONFIG_SMP 107#ifdef CONFIG_SMP
108 unsigned int cpu = thread->cpu;
109
51 /* 110 /*
52 * On SMP, if VFP is enabled, save the old state in 111 * On SMP, if VFP is enabled, save the old state in
53 * case the thread migrates to a different CPU. The 112 * case the thread migrates to a different CPU. The
@@ -74,25 +133,10 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
74 return NOTIFY_DONE; 133 return NOTIFY_DONE;
75 } 134 }
76 135
77 vfp = &thread->vfpstate; 136 if (cmd == THREAD_NOTIFY_FLUSH)
78 if (cmd == THREAD_NOTIFY_FLUSH) { 137 vfp_thread_flush(thread);
79 /* 138 else
80 * Per-thread VFP initialisation. 139 vfp_thread_exit(thread);
81 */
82 memset(vfp, 0, sizeof(union vfp_state));
83
84 vfp->hard.fpexc = FPEXC_EN;
85 vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
86
87 /*
88 * Disable VFP to ensure we initialise it first.
89 */
90 fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
91 }
92
93 /* flush and release case: Per-thread VFP cleanup. */
94 if (last_VFP_context[cpu] == vfp)
95 last_VFP_context[cpu] = NULL;
96 140
97 return NOTIFY_DONE; 141 return NOTIFY_DONE;
98} 142}
@@ -153,10 +197,13 @@ static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_
153 } 197 }
154 198
155 /* 199 /*
156 * Update the FPSCR with the additional exception flags. 200 * If any of the status flags are set, update the FPSCR.
157 * Comparison instructions always return at least one of 201 * Comparison instructions always return at least one of
158 * these flags set. 202 * these flags set.
159 */ 203 */
204 if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))
205 fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V);
206
160 fpscr |= exceptions; 207 fpscr |= exceptions;
161 208
162 fmxr(FPSCR, fpscr); 209 fmxr(FPSCR, fpscr);
@@ -381,54 +428,60 @@ static void vfp_pm_init(void)
381static inline void vfp_pm_init(void) { } 428static inline void vfp_pm_init(void) { }
382#endif /* CONFIG_PM */ 429#endif /* CONFIG_PM */
383 430
384/* 431void vfp_sync_hwstate(struct thread_info *thread)
385 * Synchronise the hardware VFP state of a thread other than current with the
386 * saved one. This function is used by the ptrace mechanism.
387 */
388#ifdef CONFIG_SMP
389void vfp_sync_state(struct thread_info *thread)
390{ 432{
433 unsigned int cpu = get_cpu();
434
391 /* 435 /*
392 * On SMP systems, the VFP state is automatically saved at every 436 * If the thread we're interested in is the current owner of the
393 * context switch. We mark the thread VFP state as belonging to a 437 * hardware VFP state, then we need to save its state.
394 * non-existent CPU so that the saved one will be reloaded when
395 * needed.
396 */ 438 */
397 thread->vfpstate.hard.cpu = NR_CPUS; 439 if (last_VFP_context[cpu] == &thread->vfpstate) {
440 u32 fpexc = fmrx(FPEXC);
441
442 /*
443 * Save the last VFP state on this CPU.
444 */
445 fmxr(FPEXC, fpexc | FPEXC_EN);
446 vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
447 fmxr(FPEXC, fpexc);
448 }
449
450 put_cpu();
398} 451}
399#else 452
400void vfp_sync_state(struct thread_info *thread) 453void vfp_flush_hwstate(struct thread_info *thread)
401{ 454{
402 unsigned int cpu = get_cpu(); 455 unsigned int cpu = get_cpu();
403 u32 fpexc = fmrx(FPEXC);
404 456
405 /* 457 /*
406 * If VFP is enabled, the previous state was already saved and 458 * If the thread we're interested in is the current owner of the
407 * last_VFP_context updated. 459 * hardware VFP state, then we need to save its state.
408 */ 460 */
409 if (fpexc & FPEXC_EN) 461 if (last_VFP_context[cpu] == &thread->vfpstate) {
410 goto out; 462 u32 fpexc = fmrx(FPEXC);
411 463
412 if (!last_VFP_context[cpu]) 464 fmxr(FPEXC, fpexc & ~FPEXC_EN);
413 goto out;
414 465
415 /* 466 /*
416 * Save the last VFP state on this CPU. 467 * Set the context to NULL to force a reload the next time
417 */ 468 * the thread uses the VFP.
418 fmxr(FPEXC, fpexc | FPEXC_EN); 469 */
419 vfp_save_state(last_VFP_context[cpu], fpexc); 470 last_VFP_context[cpu] = NULL;
420 fmxr(FPEXC, fpexc); 471 }
421 472
473#ifdef CONFIG_SMP
422 /* 474 /*
423 * Set the context to NULL to force a reload the next time the thread 475 * For SMP we still have to take care of the case where the thread
424 * uses the VFP. 476 * migrates to another CPU and then back to the original CPU on which
477 * the last VFP user is still the same thread. Mark the thread VFP
478 * state as belonging to a non-existent CPU so that the saved one will
479 * be reloaded in the above case.
425 */ 480 */
426 last_VFP_context[cpu] = NULL; 481 thread->vfpstate.hard.cpu = NR_CPUS;
427 482#endif
428out:
429 put_cpu(); 483 put_cpu();
430} 484}
431#endif
432 485
433#include <linux/smp.h> 486#include <linux/smp.h>
434 487
@@ -481,7 +534,7 @@ static int __init vfp_init(void)
481 */ 534 */
482 elf_hwcap |= HWCAP_VFP; 535 elf_hwcap |= HWCAP_VFP;
483#ifdef CONFIG_VFPv3 536#ifdef CONFIG_VFPv3
484 if (VFP_arch >= 3) { 537 if (VFP_arch >= 2) {
485 elf_hwcap |= HWCAP_VFPv3; 538 elf_hwcap |= HWCAP_VFPv3;
486 539
487 /* 540 /*